I'm following a Book which shows you how to edit a users role. Everything's working fine until I get to a line of code. I think it will be a pretty simple solution, however, I cannot figure it out.
var result = await _userManager.AddToRoleAsync(user.Id,
selectedRole.Except(userRoles).ToArray<string>());
The line of code above is returning an error:
"Argument 2: cannot convert from 'string[]' to 'string'"
In the book, it shows:
.ToArray<string>
However, in my line of code, it's just telling me to refactor to:
.ToArray());
Is there another way of converting to string? I'll post the code below, thanks.
EDIT USER CODE
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditUser(EditUserViewModel model, params string[] selectedRole)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByIdAsync(model.Id);
if (user == null)
{
return HttpNotFound();
}
user.UserName = model.UserName;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
var userRoles = await _userManager.GetRolesAsync(user.Id);
selectedRole = selectedRole ?? new string[] { };
var result = await _userManager.AddToRoleAsync(user.Id,
selectedRole.Except(userRoles).ToArray<string>()); // may not be right
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
result = await _userManager.RemoveFromRoleAsync(user.Id, userRoles.Except(selectedRole).ToArray().ToString()); // put to string
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
return RedirectToAction("Index");
}
ModelState.AddModelError("", "Something's failed.");
return View();
}
EditUserViewModel
public class EditUserViewModel
{
public string Id { get; set; }
[Required]
[Display(Name = "Username")]
public string UserName { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
public IEnumerable<SelectListItem> RolesList { get; set; }
}
EditUser View
<div class="form-group">
#Html.Label("Roles", new { #class = "control-label col-md-2" })
<span class="col-md-10">
#foreach (var item in Model.RolesList)
{
<input type="checkbox" name="SelectedRole" value="#item.Value"
checked="#item.Selected" class="checkbox-inline" />
#Html.Label(item.Value, new { #class = "control-label" })
}
</span>
</div>
If you want to convert to string you can use String.Join and as a separator use
string.Empty, null or whatever separator you want but will be included in your string.
string str = string.Join(string.Empty, yourArray)
It's a typo in the book or when copying from the book. It should be:
var result = await _userManager.AddToRolesAsync(user.Id,
selectedRole.Except(userRoles).ToArray<string>());
The AddToRolesAsync takes an IEnumerable<string> as its second parameter, AddToRoleAsync takes a string.
Related
I have a problem all the time filename returns null in controller.There are no options for how to solve this, I reviewed the entire Internet
Screen: https://drive.google.com/file/d/1p6om2yBJ8sED_8DP0yB1AKEPYlcFXq_A/view?usp=sharing
][2]
public int CategoryId { get; set; }
[Required(ErrorMessage = "Enter category name")]
[MaxLength(100, ErrorMessage = "MaxLength 100 symbols")]
[MinLength(5, ErrorMessage = "MinLength 5 symbols")]
public string CategoryName { get; set; }
[Required(ErrorMessage = "Enter category description")]
[MaxLength(500, ErrorMessage = "MaxLength 500 symbols")]
[MinLength(100, ErrorMessage = "MaxLength 100 symbols")]
public string CategoryDiscription { get; set; }
public string FileName { get; set; }
[NotMapped]
public IFormFile File { get; set; }
public async Task<ActionResult> Create([Bind("CategoryId","CategoryName","formFile")] CategoriesModel categories)
{
try
{
string rootpatch = _webHostEnvironment.WebRootPath;
string filename = Path.GetFileNameWithoutExtension(categories.File.FileName);
string extension = Path.GetExtension(categories.File.FileName);
categories.FileName = filename + DateTime.Now.ToString("yy.MMM.dd") + extension;
string patch = Path.Combine(rootpatch + "/image/", filename);
using (var fileStream = new FileStream(patch, FileMode.Create))
{
await categories.File.CopyToAsync(fileStream);
}
_context.Add(categories);
await _context.SaveChangesAsync();
return RedirectToAction();
}
catch
{
return View();
}
}
<form asp-action="Create" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="File" class="control-label"></label>
<input asp-for="File" class="form-control" type = "file" accept = "image/*"/>
<span asp-validation-for="File" class="text-danger"></span>
</div>
</form>
You use [Bind("CategoryId","CategoryName","formFile")] ,so the File property will not be binded to CategoriesModel categories.Here is the official doc about Bind attribute.
You can try to remove [Bind("CategoryId","CategoryName","formFile")],and make sure the name of Input is File in your view.
On create action i'm allowing the user to upload a picture file, and based on the following view model if it is invalid during post action the message appear and the model is repopulated again with user's input except the file.
i'm not sure if i can repopulate the file when the model is invalid
ViewModel
public class CreateEditChildViewModel
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string DateOfBirth { get; set; }
[Required]
public string Gender { get; set; }
[Required]
public string EducationalLevel { get; set; }
public string Picture { get; set; }
public string Country { get; set; }
public string Region { get; set; }
public string City { get; set; }
}
Post Action
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateEditChildViewModel model)
{
if (ModelState.IsValid)
{
string webRootPath = _hostingEnvironment.WebRootPath;
var files = HttpContext.Request.Form.Files;
var child = new Childs()
{
FirstName = model.FirstName,
LastName = model.LastName,
ParentOfId = model.ParentId,
SpecialistOfId = model.SpecialistId,
DateOfBirth = model.DateOfBirth,
Gender = model.Gender,
...................
};
_db.Childs.Add(child);
await _db.SaveChangesAsync();
if (files.Count() > 0)
{
var uploads = Path.Combine(webRootPath, "images");
var extension = Path.GetExtension(files[0].FileName);
using (var filesStream = new FileStream(Path.Combine(uploads, child.Id + extension), FileMode.Create))
{
files[0].CopyTo(filesStream);
}
child.Picture = #"\images\" + child.Id + extension;
}
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
CreateEditChildViewModel modelVM = new CreateEditChildViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
....................
//All model properties
};
return View(modelVM);
}
To sum up my question i would like to save the file input in case if the model is invalid i don't make the user to re-upload the file
You can use Ajax partial refresh to upload data to the backend, which will keep the state of the client page. The error can be judged by h5 or customlize error.
Here is the example(take the FirstName and LastName as examples).
ViewModel(Add File attribute):
public class CreateEditChildViewModel
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
//...
public IFormFile File { get; set; }
}
Your View:
<form method="post" id="myForm" enctype="multipart/form-data">
<div id="error"></div>
<div asp-validation-summary="All"></div>
<label asp-for="#Model.FirstName"></label>
<input asp-for="#Model.FirstName" />
<label asp-for="#Model.LastName"></label>
<input asp-for="#Model.LastName" />
<input asp-for="#Model.File" />
<input type="submit" value="submit" id="sub" />
</form>
#section Scripts{
<script>
$('#myForm').submit(function (e) {
e.preventDefault();
var formData = new FormData($('#myForm')[0]);
$.ajax({
method: 'post',
url: "/Home/Create",
data: formData,
processData: false,
contentType: false,
success: function (data) {
console.log(data)
if (data == 'Index') {
location.href = '/home/index'
} else {
$('#error').text('please fill in valid data')
}
},
error: function (e) {
}
})
})
</script>
}
Post Action(Change the return data of the background to json):
[HttpPost]
public async Task<IActionResult> Create(CreateEditChildViewModel model)
{
if (ModelState.IsValid)
{
string webRootPath = _hostingEnvironment.WebRootPath;
var files = HttpContext.Request.Form.Files;
var child = new Childs()
{
FirstName = model.FirstName,
LastName = model.LastName,
//...................
};
_db.Childs.Add(child);
await _db.SaveChangesAsync();
if (files.Count() > 0)
{
var uploads = Path.Combine(webRootPath, "images");
var extension = Path.GetExtension(files[0].FileName);
using (var filesStream = new FileStream(Path.Combine(uploads, child.Id + extension), FileMode.Create))
{
files[0].CopyTo(filesStream);
}
child.Picture = #"\images\" + child.Id + extension;
}
await _db.SaveChangesAsync();
return Json("Index");
}
CreateEditChildViewModel modelVM = new CreateEditChildViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
//....................
};
return Json(modelVM);
}
Result:
I have WebAPI based project. It has an API for user registration. RegistrationModel looks as below.
public class RegistrationTO
{
[Required(ErrorMessage="Email required")]
public string Email { get; set; }
[Required(ErrorMessage="Name required")]
public string Name { get; set; }
[Required(ErrorMessage="Surname required")]
public string Surname { get; set; }
}
API
public string Post([FromBody]RegistrationTO model)
{
if (ModelState.IsValid)
{
//actual stuff
}
else
{
return "all * marked fields are required";
}
}
My requirement if model have Email as null, I should return message
"email required", if Name is null then "Name required" & so on.
Yes,one patchy way would be :-
public string Post([FromBody]UserRegistrationTO model)
{
if (ModelState.IsValid)
{
//actual stuff
}
else
{
if(model.Email == null)
return "Email required";
else if(model.Name == null)
return "name required"; // & so on.
}
}
Is there any other simple way to do this??
try do that:
if (ModelState.IsValid)
{
// do something
}
else
return "The following errors happen: \n" +
ModelState.SelectMany(s => s.Value.Errors)
.Select(s => s.ErrorMessage)
.Aggregate((seed, currentValue) => seed += "\n" + currentValue);
I hope it can help you.
You can do this at the view itself.
For individual properties :
#Html.ValidationMessageFor(x => x.Email, null, new { #class = "text-danger" })
and for all failed validation
#if (!ViewData.ModelState.IsValid)
{
<div class="alert alert-danger">
#Html.ValidationSummary(false)
</div>
}
I'm having trouble finding a tutorial / video that shows how to implement Cascading DropDownList from a Database using EntityFramework. I'm using ASP.NET MVC Core, EntityFramework Core with C#.
As of now, I'm able to retrieve the data from my database to my 3 DropDownList fine.
What I would like to be able to accomplish is to have the user select a State first which would then display all Cities related to that State. Then after user has selected a City it would display the Zip Code(s) related to the City.
Any help would be greatly appreciated.
Models
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int StateId { get; set; }
public int CityId { get; set; }
public int ZipId { get; set; }
public State State { get; set; }
public City City { get; set; }
public Zip Zip { get; set; }
}
public class State
{
public int StateId { get; set; }
public string Abbr { get; set; }
public List<Customer> Customers { get; set; }
}
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
public int StateId { get; set; }
public State State { get; set; }
public List<Customer> Customers { get; set; }
}
public class Zip
{
public int ZipId { get; set; }
public string PostalCode { get; set; }
public int CityId { get; set; }
public City City { get; set; }
public List<Customer> Customers { get; set; }
}
ViewModels
public class CustomerFormVM
{
public int CustomerId { get; set; }
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
[Required(ErrorMessage = "Select State")]
[Display(Name = "State")]
public int StateId { get; set; }
//public IEnumerable<State> States { get; set; }
public IEnumerable<SelectListItem> States { get; set; }
[Required(ErrorMessage = "Select City")]
[Display(Name = "City")]
public int CityId { get; set; }
//public IEnumerable<City> Citys { get; set; }
public IEnumerable<SelectListItem> Citys { get; set; }
[Required(ErrorMessage = "Select Zip")]
[Display(Name = "Zip")]
public int ZipId { get; set; }
//public IEnumerable<Zip> Zips { get; set; }
public IEnumerable<SelectListItem> Zips { get; set; }
}
CustomerController
public class CustomerController : Controller
{
private MultiDbContext db;
public CustomerController(MultiDbContext context)
{
db = context;
}
// GET: /<controller>/
public IActionResult Index()
{
return View(db.Customers.ToList());
}
public IActionResult getCititesFromDatabaseByStateId(int id)
{
return View(db.Citys.Where(c => c.StateId == id).ToList());
}
public IActionResult getCities(int id)
{
var cities = new List<City>();
cities = getCititesFromDatabaseByStateId(id); //call repository
return Json(cities);
}
public ActionResult Create()
{
var states = db.States.ToList();
var citys = db.Citys.ToList();
var zips = db.Zips.ToList();
var viewModel = new CustomerFormVM
{
States = states,
Citys = citys,
Zips = zips
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CustomerFormVM vm)
{
if (ModelState.IsValid)
{
var customer = new Customer();
{
customer.FirstName = vm.FirstName;
customer.LastName = vm.LastName;
customer.StateId = vm.StateId;
customer.CityId = vm.CityId;
customer.ZipId = vm.ZipId;
}
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
vm.States = db.States.ToList();
vm.Citys = db.Citys.ToList();
vm.Zips = db.Zips.ToList();
return View(vm);
}
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var customervm = new CustomerFormVM();
{
Customer customer = db.Customers.SingleOrDefault(c => c.CustomerId == id);
if (customer == null)
{
return NotFound();
}
customervm.CustomerId = customer.CustomerId;
customervm.FirstName = customer.FirstName;
customervm.LastName = customer.LastName;
// Retrieve list of States
var states = db.States.ToList();
customervm.States = states;
// Retrieve list of Citys
var citys = db.Citys.ToList();
customervm.Citys = citys;
// Retrieve list of Citys
var zips = db.Zips.ToList();
customervm.Zips = zips;
// Set the selected state
customervm.StateId = customer.StateId;
// Set the selected city
customervm.CityId = customer.CityId;
// Set the selected zip
customervm.ZipId = customer.ZipId;
}
return View(customervm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomerFormVM vmEdit)
{
if (ModelState.IsValid)
{
Customer customer = db.Customers.SingleOrDefault(c => c.CustomerId == vmEdit.CustomerId);
if (customer == null)
{
return NotFound();
}
customer.FirstName = vmEdit.FirstName;
customer.LastName = vmEdit.LastName;
customer.StateId = vmEdit.StateId;
customer.CityId = vmEdit.CityId;
customer.ZipId = vmEdit.ZipId;
db.Entry(customer).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vmEdit);
}
}
Create View
<div class="form-group">
#Html.LabelFor(c => c.FirstName)
#Html.TextBoxFor(c => c.FirstName, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(c => c.LastName)
#Html.TextBoxFor(c => c.LastName, new { #class = "form-control" })
</div>
<div class="form-group">
#*#Html.LabelFor(s => s.StateId)
#Html.DropDownListFor(s => s.StateId, new SelectList(Model.States, "StateId", "Abbr"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(s => s.StateId)*#
<label asp-for="StateId "></label>
<select asp-for="StateId " asp-items="Model.States" class="form-control" id="state-target"></select>
<span asp-validation-for="StateId " class="text-danger"></span>
</div>
<div class="form-group">
#*#Html.LabelFor(ct => ct.CityId)
#Html.DropDownListFor(ct => ct.CityId, new SelectList(Model.Citys, "CityId", "Name"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(ct => ct.CityId)*#
<label asp-for="CityId"></label>
<select asp-for="CityId" asp-items="Model.Citys" class="form-control" id="city-target"></select>
<span asp-validation-for="CityId" class="text-danger"></span>
</div>
<div class="form-group">
#Html.LabelFor(z => z.ZipId)
#Html.DropDownListFor(z => z.ZipId, new SelectList(Model.Zips, "ZipId", "PostalCode"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(z => z.ZipId)
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
}
#section scripts {
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
<script src="~/lib/js/example.js"></script>,
}
I had a similar situation but in my example I have a Root folder and depending on which root folder I am using the next drop down list would display the corresponding sub-folders.
Not sure if there is a purly asp.net solution but, I used Jquery/Ajax for this.
Your code should look something like this:
html list:
<label asp-for="StateId "></label>
<select asp-for="StateId " asp-items="Model.States" class="form-control" id="state-target"></select>
<span asp-validation-for="StateId " class="text-danger"></span>
<label asp-for="CityId"></label>
<select asp-for="CityId" asp-items="Model.Citys" class="form-control" id="city-target"></select>
<span asp-validation-for="CityId" class="text-danger"></span>
Jquery code, you write this in .js file and then add it to a specific view with this statement<script src="~/js/example.js"></script>, Don't forget you need to add a jquery library to your project before any other javascript, and your example.js will contain:
$(document).ready(function () {
$("#state-target").on("change", function () {
$list = $("#city-target");
$.ajax({
url: "/getCities",
type: "GET",
data: { id: $("#state-target").val() }, //id of the state which is used to extract cities
traditional: true,
success: function (result) {
$list.empty();
$.each(result, function (i, item) {
$list.append('<option value="' + item["CityId"] + '"> ' + item["Name"] + ' </option>');
});
},
error: function () {
alert("Something went wrong call the police");
}
});
});
});
The Ajax request will call this action in the Controller which will retrieve a list of cities from the database (using something like return dbContext.CityTable.Where(c => c.StateId == id).ToList() inside a getCititesFromDatabaseByStateId(id) method) and then return the Json object, the success function will create a list of options and apply it:
public IActionResult getCities(int id)
{
var cities = new List<City>();
cities = getCititesFromDatabaseByStateId(id); //call repository
return Json(citites);
}
In your ViewModel consider changing IEnumerable<State/City/Zip> (IEnumerable<T>) to IEnumerable<SelectListItem>. I can say as well your Model's are messy (but if you can get data the from the database focus on getting the list working 1st), consider improving them later.
Fix for 2 errors mentioned in the comments:
public List<City> getCititesFromDatabaseByStateId(int id)
{
return db.Citys.Where(c => c.StateId == id).ToList();
}
public ActionResult Create()
{
var states = new SelectList(db.States.ToList(), "StateId", "Abbr");
var citys = new SelectList(db.Citys.ToList(), "CityId", "Name");
var zips = new SelectList(db.Zips.ToList(), "ZipId", "Code");
var viewModel = new CustomerFormVM
{
States = states,
Citys = citys,
Zips = zips
};
return View(viewModel);
}
When I try to create a ticket on my create.cshtml it gives me this error. I think it's because my Category uses a dropdownlist using ViewBags while my User and Administrator dropdown list doesn't.
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.Ticket_dbo.Category_CategoryID". The conflict occurred in
database "RecreationalServicesTicketingSystem.DAL.IssueContext", table
"dbo.Category", column 'CategoryID'. The statement has been
terminated.
TicketController.cs
public class TicketController : Controller
{
private IssueContext db = new IssueContext();
public ActionResult Create()
{
TicketVM model = new TicketVM();
ConfigureViewModel(model);
ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "CategoryName");
ViewBag.AllUsers = db.Users.ToList().Select(u => new SelectListItem() { Value = u.UserID.ToString(), Text = string.Format("{0} {1}", u.FirstMidName, u.LastName) });
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(TicketVM model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
Ticket ticket = new Ticket
{
Issue = model.Issue,
IssuedTo = model.IssuedTo
};
if (ModelState.IsValid)
{
db.Tickets.Add(ticket);
ERROR------> db.SaveChanges(); <------ ERROR
return View();
}
ViewBag.CategoryID = new SelectList(db.Categories, "CategoryID", "CategoryName", ticket.CategoryID);
ViewBag.AllUsers = db.Users.ToList().Select(u => new SelectListItem() { Value = u.UserID.ToString(), Text = string.Format("{0} {1}", u.FirstMidName, u.LastName) });
ViewBag.AllAdmins = db.Users.Where(u => u.IsAdministrator).Include(u => u.Tickets);
return View(ticket);
}
private void ConfigureViewModel(TicketVM model)
{
IEnumerable<User> admins = db.Users.Where(u => u.IsAdministrator).OrderBy(u => u.LastName);
model.AdministratorList = admins.Select(a => new SelectListItem
{
Value = a.UserID.ToString(),
Text = string.Format("{0} {1}", a.FirstMidName, a.LastName)
});
}
}
\Views\Ticket\Create.cshtml
#model RecreationalServicesTicketingSystem.ViewModels.TicketVM
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Ticket</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CategoryID, "Category")
</div>
<div class="editor-field">
#Html.DropDownList("CategoryID", String.Empty)
#Html.ValidationMessageFor(model => model.CategoryID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.UserID, "User")
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.UserID, (IEnumerable<SelectListItem>)ViewBag.AllUsers, "Please select")`
#Html.ValidationMessageFor(model => model.UserID)
</div>
<div class="editor-field">
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.UserID)
<div class="form-group">
#Html.LabelFor(m => m.IssuedTo)
#Html.DropDownListFor(m => m.IssuedTo, Model.AdministratorList, "Please select", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.IssuedTo)
</div>
<div class="form-group">
#Html.LabelFor(m => m.Issue)
#Html.TextBoxFor(m => m.Issue, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Issue)
</div>
}
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Category.cs
public class Category
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Ticket> Tickets { get; set; }
}
Ticket.cs
public enum Priority
{
Low, Med, High
}
public class Ticket
{
public int? TicketID { get; set; }
[Required(ErrorMessage = "Please enter the description")]
public string Issue { get; set; }
[Display(Name = "Administrator")]
[Required(ErrorMessage = "Please select the Administrator")]
public int IssuedTo { get; set; }
public int Author { get; set; }
[DisplayFormat(NullDisplayText = "No Priority")]
public Priority? Priority { get; set; }
[ForeignKey("CategoryID")]
public virtual Category Category { get; set; }
public int CategoryID { get; set; }
public int UserID { get; set; }
[ForeignKey("UserID")]
public virtual User User { get; set; }
}
ViewModels\TicketVM.cs
public class TicketVM
{
public int? UserID { get; set; }
[Required(ErrorMessage = "Please enter the description")]
public string Issue { get; set; }
[Display(Name = "Administrator")]
[Required(ErrorMessage = "Please select the Administrator")]
public int IssuedTo { get; set; }
public IEnumerable<SelectListItem> AdministratorList { get; set; }
public int CategoryID { get; set; }
}
AccountController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using Microsoft.Web.WebPages.OAuth;
using WebMatrix.WebData;
using RecreationalServicesTicketingSystem.Filters;
using RecreationalServicesTicketingSystem.Models;
namespace RecreationalServicesTicketingSystem.Controllers
{
[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
WebSecurity.Logout();
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Disassociate(string provider, string providerUserId)
{
string ownerAccount = OAuthWebSecurity.GetUserName(provider, providerUserId);
ManageMessageId? message = null;
// Only disassociate the account if the currently logged in user is the owner
if (ownerAccount == User.Identity.Name)
{
// Use a transaction to prevent the user from deleting their last login credential
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Serializable }))
{
bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
if (hasLocalAccount || OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name).Count > 1)
{
OAuthWebSecurity.DeleteAccount(provider, providerUserId);
scope.Complete();
message = ManageMessageId.RemoveLoginSuccess;
}
}
}
return RedirectToAction("Manage", new { Message = message });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: "";
ViewBag.HasLocalPassword = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
//
// POST: /Account/Manage
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(LocalPasswordModel model)
{
bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
ViewBag.HasLocalPassword = hasLocalAccount;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasLocalAccount)
{
if (ModelState.IsValid)
{
// ChangePassword will throw an exception rather than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
}
else
{
// User does not have a local password so remove any validation errors caused by a missing
// OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
try
{
WebSecurity.CreateAccount(User.Identity.Name, model.NewPassword);
return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
}
catch (Exception)
{
ModelState.AddModelError("", String.Format("Unable to create local account. An account with the name \"{0}\" may already exist.", User.Identity.Name));
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
return RedirectToLocal(returnUrl);
}
if (User.Identity.IsAuthenticated)
{
// If the current user is logged in add the new account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return RedirectToLocal(returnUrl);
}
else
{
// User is new, ask for their desired membership name
string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
ViewBag.ReturnUrl = returnUrl;
return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
}
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl)
{
string provider = null;
string providerUserId = null;
if (User.Identity.IsAuthenticated || !OAuthWebSecurity.TryDeserializeProviderUserId(model.ExternalLoginData, out provider, out providerUserId))
{
return RedirectToAction("Manage");
}
if (ModelState.IsValid)
{
// Insert a new user into the database
using (UsersContext db = new UsersContext())
{
UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == model.UserName.ToLower());
// Check if user already exists
if (user == null)
{
// Insert name into the profile table
db.UserProfiles.Add(new UserProfile { UserName = model.UserName });
db.SaveChanges();
OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("UserName", "User name already exists. Please enter a different user name.");
}
}
}
ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(provider).DisplayName;
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
return View();
}
[AllowAnonymous]
[ChildActionOnly]
public ActionResult ExternalLoginsList(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return PartialView("_ExternalLoginsListPartial", OAuthWebSecurity.RegisteredClientData);
}
[ChildActionOnly]
public ActionResult RemoveExternalLogins()
{
ICollection<OAuthAccount> accounts = OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name);
List<ExternalLogin> externalLogins = new List<ExternalLogin>();
foreach (OAuthAccount account in accounts)
{
AuthenticationClientData clientData = OAuthWebSecurity.GetOAuthClientData(account.Provider);
externalLogins.Add(new ExternalLogin
{
Provider = account.Provider,
ProviderDisplayName = clientData.DisplayName,
ProviderUserId = account.ProviderUserId,
});
}
ViewBag.ShowRemoveButton = externalLogins.Count > 1 || OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
return PartialView("_RemoveExternalLoginsPartial", externalLogins);
}
#region Helpers
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
public enum ManageMessageId
{
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
}
internal class ExternalLoginResult : ActionResult
{
public ExternalLoginResult(string provider, string returnUrl)
{
Provider = provider;
ReturnUrl = returnUrl;
}
public string Provider { get; private set; }
public string ReturnUrl { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl);
}
}
private static string ErrorCodeToString(MembershipCreateStatus createStatus)
{
// See http://go.microsoft.com/fwlink/?LinkID=177550 for
// a full list of status codes.
switch (createStatus)
{
case MembershipCreateStatus.DuplicateUserName:
return "User name already exists. Please enter a different user name.";
case MembershipCreateStatus.DuplicateEmail:
return "A user name for that e-mail address already exists. Please enter a different e-mail address.";
case MembershipCreateStatus.InvalidPassword:
return "The password provided is invalid. Please enter a valid password value.";
case MembershipCreateStatus.InvalidEmail:
return "The e-mail address provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidAnswer:
return "The password retrieval answer provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidQuestion:
return "The password retrieval question provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidUserName:
return "The user name provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.ProviderError:
return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
case MembershipCreateStatus.UserRejected:
return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
default:
return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
}
}
#endregion
}
}
Models\AccountModels.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Globalization;
using System.Web.Security;
namespace RecreationalServicesTicketingSystem.Models
{
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class RegisterExternalLoginModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
public string ExternalLoginData { get; set; }
}
public class LocalPasswordModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class LoginModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ExternalLogin
{
public string Provider { get; set; }
public string ProviderDisplayName { get; set; }
public string ProviderUserId { get; set; }
}
}