I'm working with ASP.NET MVC4 and now I want to add a dropdownlist with data from my mysql database. This is what I do :
In my view (Register.cshtml):`
<div class="control-group">
#Html.LabelFor(m => m.DistrictID, new { #class= "control-label"})
<div class="controls">
#Html.DropDownListFor(model => model.DistrictID, new SelectList(ViewBag.Districts, "district_id", "district_name", Model.DistrictID))
</div>
</div>
In my Controller (AccountController):
[AllowAnonymous]
public ActionResult Register()
{
var districts = repository.GetDistricts();
ViewBag.Districts = districts;
return View(new RegisterModel());
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the User
try
{
MembershipService.CreateUser(model.Name, model.FamilyName, model.BirthDate, model.Sex, model.Nationality, model.Email, model.UserName, model.Password, model.Street, model.StreetNr);
FormsAuthentication.SetAuthCookie(model.UserName, false);
return RedirectToAction("Index", "Home");
}
catch (ArgumentException ae)
{
ModelState.AddModelError("", ae.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I get the Districts from my Repository like this:
public IQueryable<district> GetDistricts()
{
return from district in entities.districts
orderby district.district_name
select district;
}
My RegisterModel:
public class RegisterModel
{
[Required]
[Display(Name = "Given name")]
public string Name { get; set; }
[Required]
[Display(Name = "Family name")]
public string FamilyName { get; set; }
[Required]
[Display(Name = "Birthdate")]
public DateTime BirthDate { get; set; }
[Required]
[Display(Name = "Sex")]
public string Sex { get; set; }
[Required]
[Display(Name = "Nationality")]
public string Nationality { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[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; }
[Required]
[Display(Name = "Street")]
public string Street { get; set; }
[Required]
[Display(Name = "Street Number")]
public int StreetNr { get; set; }
[Required]
[Display(Name = "District")]
public IEnumerable<SelectListItem> Districts { get; set; }
public int DistrictID { get; set; }
}
The dropdownlist is filled with districts but when I click on "Register" I get this error:
Value cannot be null.
Parameter name: items
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: items
When I debug the method I see that the ModelState is NOT valid.
Key 11 is DistrictID and includes the districtid but Key 12 is Districts and gives the error: "The district field is required" ...
What am I doing wrong?
Consider the case when model validation fails. View will be redisplayed again with model sent with the request. However this line:
new SelectList(ViewBag.Districts, "district_id", "district_name", Model.Districts)
will have null as a first parameter, since ViewBag.Districts was not repopulated, causing the exception. So in order to avoid exception just set this property again:
// If we got this far, something failed, redisplay form
var districts = repository.GetDistricts();
ViewBag.Districts = districts;
return View(model);
Update. When seeing the model definition, thing that immediately comes into the mind is Required attribute of the Districts collection. Most likely you do not need user to enter any of those, nor you save them into the database. Try removing this attribute, and error about this property will disappear.
Related
I'm new to MVC and have been pulling my hair out with this one. I'm already going bold so need someone to rescue what's left!
I'm using asp.net Identity and trying to create a one-to-one relationship between RegisterViewModel and StoreDetails and really hope someone can help me out!
Here is my RegisterViewModel:
public class RegisterViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { 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 virtual StoreDetails StoreDetails { get; set; }
}
Here is my StoreDetails model:
public class StoreDetails
{
[Key, ForeignKey("RegisterViewModel")]
public string StoreName { get; set; }
public virtual RegisterViewModel RegisterViewModel { get; set; }
}
Controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser {
// PERSONAL DETAILS
UserName = model.Email,
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName
};
var storeDetails = new StoreDetails {
// STORE DETAILS
StoreName = model.StoreDetails.StoreName
};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
// UPDATE DB WITH STORE DETAILS DATA
var db = new ApplicationDbContext();
db.StoreDetails.Add(new StoreDetails
{
StoreName = model.StoreDetails.StoreName
});
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
What i'm looking to achieve is on registration the StoreDetails table is generated with the following input:
#Html.TextBoxFor(m => m.StoreDetails.StoreName)
If you need any further details please ask.
Thanks
RegisterViewModel is not a real entity i.e person that gets saved into the database by EF. All it is is a carrier object that passes the values of email and password into the real User/person object called ApplicationUser. This is a view-model and not a real model.
in your example you added StoreDetails in the RegisterViewModel which is correct. But you also need to add this in your ApplicationUser class as a property.
public class ApplicationUser {
public virtual StoreDetails StoreDetails { get; set; }
Then run your migrations.
Adding anything to the RegisterViewModel is made to pass data from the view layer in a way to safe guard the object thats going into your db.
i.e
doing this doesnt do anything because the viewmodel doesnt live in the db
public class StoreDetails
{
[Key, ForeignKey("RegisterViewModel")] //this will not work remove
public string StoreName { get; set; }
public virtual RegisterViewModel RegisterViewModel { get; set; }// remove
}
public Ienumerable<StoreDetails> StoreDetails { get; set; }
public int StoreDetailsId { get; set; }
In controller:
using (var db = ApplicationDbContext.Create())
{
model.StoreDetails = db.StoreDetails.ToList();
}
Successfully I added some fields in AspNetUsers table but I don't know how I can retrieve them. My project is asp.net web api and I can retrieve username from token. I want to return departmentId by given userId, or username.
and trying like but this gives me null exeption
private ApplicationUserManager _userManager;
public int GetdeptId(string username)
{
var dept = _userManager.Users.Where(d => d.UserName == username).Select(c => c.DepartmentId).FirstOrDefault();
return dept;
}
Here is the way I could create fields in my AspNetUsers and I can insert values successfully. The problem is to return values from AspNetUsers.
public class RegisterBindingModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[Display(Name = "FirstName")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Lastname")]
public string Lastname { get; set; }
[Required]
[Display(Name = "Department")]
public int DepartmentId { 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; }
}
And then the registerration
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email, Firstname = model.FirstName, LastName = model.Lastname, DepartmentId = model.DepartmentId };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
Thank you in advance!
Try!
var context = new IdentityDbContext();
var users = context.Users.ToList();
If you have access to your ApplicationDbContext as say, _db, then you can cast it to IdentityDbContext and access the tables through that. e.g.
IdentityDbContext context = _db as IdentityDbContext;
IEnumerable<User> users = context.Users;
Having problem with DropdownListFor when it passes the (ModelState.IsValid). when it passes the DropdownList, the error (Object reference not set to an instance of an object.) occurs
VIEW
Error in this line of the view
#Html.DropDownListFor(model => model.si_g_code, new= SelectList(Model.Guardian,"g_ref_code","fullname"), "Select Guardian")
#Html.ValidationMessageFor(model => model.si_g_code)
#Html.DropDownListFor(model => model.si_gl_id, new SelectList(Model.GradeLevel,"gl_id","gl_name"), "Select Grade Level", new { id = "ddlGrade" })
#Html.ValidationMessageFor(model => model.si_gl_id)
Controller
[HttpGet]
public ActionResult RegisterStudent()
{
Models.ModelActions action = new Models.ModelActions();
var model = new acgs_qm.Models.CreateStudent
{
GradeLevel = action.getGrade(),
Guardian = action.getGuardian(),
si_id = action.getStudentNum()
};
return View(model);
}
[HttpPost]
public ActionResult RegisterStudent(CreateStudent Create)
{
acgs_qm.Models.ModelActions Ma = new acgs_qm.Models.ModelActions();
if (ModelState.IsValid)
{
Ma.insertStudent(Create);
}
return View();
}
Model
public class CreateStudent
{
[DisplayAttribute(Name = "Student ID")]
public string si_id { get; set; }
[Required]
[DisplayAttribute(Name = "First Name")]
public string si_fname { get; set; }
[Required]
[DisplayAttribute(Name = "Middle Name")]
public string si_mname { get; set; }
[Required]
[DisplayAttribute(Name = "Last Name")]
public string si_lname { get; set; }
[DataType(DataType.Text)]
[Required]
[DisplayAttribute(Name = "Contact Number")]
public string si_contact_no { get; set; }
[Required]
[DisplayAttribute(Name = "Gender")]
public string si_gender { get; set; }
[Required]
[DisplayAttribute(Name = "Civil Status")]
public string si_civil_status { get; set; }
[Required]
[DisplayAttribute(Name = "Birthdate")]
public string si_birthdate { get; set; }
[Required]
[DisplayAttribute(Name = "Birth Place")]
public string si_brith_place { get; set; }
[Required]
[DisplayAttribute(Name = "Guardian")]
public string si_g_code { get; set; }
[Required]
[DisplayAttribute(Name = "Enrolled")]
public string si_enrolled { get; set; }
[Required]
[DisplayAttribute(Name = "Email")]
public string si_email { get; set; }
[Required]
[DisplayAttribute(Name = "Grade Level")]
public int si_gl_id { get; set; } //fk
[Required]
[DisplayAttribute(Name = "Section")]
public int si_sec_id { get; set; } //fk
public IEnumerable<GradeLevel> GradeLevel { get; set; }
public IEnumerable<Guardian> Guardian { get; set; }
}
public class GradeLevel
{
public string gl_id { get; set; }
public string gl_roman_no { get; set; }
public string gl_name { get; set; }
}
public class Guardian
{
public string g_ref_code { get; set; }
public string g_fname { get; set; }
public string g_mname { get; set; }
public string g_lname { get; set; }
public string g_contact { get; set; }
public string fullName { get; set; }
}
Help is much appreciated :)
The problem is here:
#Html.DropDownListFor(model => model.si_gl_id, new SelectList(Model.GradeLevel,"gl_id","gl_name"), "Select Grade Level", new { id = "ddlGrade" })
The SelectList takes Model.GradeLevel as parameter which is null
So you have to change the code like this.
[HttpPost]
public ActionResult RegisterStudent(CreateStudent Create)
{
acgs_qm.Models.ModelActions Ma = new acgs_qm.Models.ModelActions();
if (ModelState.IsValid)
{
Ma.insertStudent(Create);
}
//Populate the values so that they wont be null in selectlist
Create.GradeLevel = action.getGrade();
Create.Guardian = action.getGuardian();
return View(Create);
}
Since in your view if dropdownlist is not selected then the model field will be null. So while passing to view in post method explicitly assign these values because always the dropdownlist needs the value.
Only if the model is passed to view,the object reference is set. Here in this code, when you are passing the Create to the view the reference of the model object is set to the instance of the Create object.
This works for sure I checked. Wherever there is Selectlist you have to make sure the value passed is not null. For that purpose only i am populating
GradeLevel and Guardian
Http is stateless. That means it will not keep your data between your 2 requests.
In your GET action method, you are loading data for the dropdowns and sending to the view. But in your HttpPost action method, When ModelState.IsValid is false, you are returning the same view. But you are not passing any model/viewmodel to that which has data for your dropdowns. Your razor view is trying to use the GradeLevel and Guardian collection to render the dropdown ,but since we did not load the data for those properties, it is NULL. That is the reason you are getting this error.
What you should do is, before returning the view, reload the GradeLevel and Guardian property values again.
[HttpPost]
public ActionResult RegisterStudent(CreateStudent Create)
{
var Ma = new acgs_qm.Models.ModelActions();
if (ModelState.IsValid)
{
Ma.insertStudent(Create);
}
//Reload the data
Create.GradeLevel = action.getGrade(),
Create.Guardian = action.getGuardian(),
return View(Create);
}
I am fairly new to MVC5/ASP.NET Identity and I have found an issue that has stumped me a bit.
I am writing a small form for my ASP.NET MVC5 application that will allow an admin user (member of the Admins role) to review the users that have signed up to the site and edit the details of and assign roles to those users. When the form is submitted, if a role has been assigned to the user the UserManager.AddToRole method is called.
I noticed after this that once this is done, that user is then unable to log into the application. Looking in the database, it appears when AddToRole is called, the PasswordHash field is set to null. Is this normal and if not how to I get around this issue?
For reference my relevant code is below
Controller
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public ActionResult Details(EditUserViewModel model)
{
model.Save();
return RedirectToAction("List", "Account");
}
Relevant view models
public class EditUserViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
[Required]
[StringLength(255)]
[Display(Name = "Email address")]
public string Email { get; set; }
[StringLength(255)]
[Display(Name = "First name")]
public string FirstName { get; set; }
[StringLength(255)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[StringLength(255)]
[Display(Name = "Mobile Number")]
public string MobileNumber { get; set; }
public IList<EditUserRolesViewModel> UserRoles { get; set; }
public void Save()
{
using (ApplicationDbContext context = new ApplicationDbContext())
{
ApplicationUser user = new ApplicationUser()
{
Id = this.UserId,
UserName = this.UserName,
Email = this.Email,
FirstName = this.FirstName,
LastName = this.LastName,
MobileNumber = this.MobileNumber
};
context.Users.Attach(user);
context.Entry(user).Property(x => x.Email).IsModified = true;
context.Entry(user).Property(x => x.FirstName).IsModified = true;
context.Entry(user).Property(x => x.LastName).IsModified = true;
context.Entry(user).Property(x => x.MobileNumber).IsModified = true;
context.SaveChanges();
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
foreach (var row in this.UserRoles)
{
if (row.RowChanged)
{
if (row.RoleAssigned)
{
UserManager.AddToRole(this.UserId, row.RoleName);
}
else
{
UserManager.RemoveFromRole(this.UserId, row.RoleName);
}
}
}
}
}
}
public class EditUserRolesViewModel
{
public string RoleId { get; set; }
[Display(Name = "Role name")]
public string RoleName { get; set; }
[Display(Name = "Assigned")]
public bool RoleAssigned { get; set; }
public bool RowChanged { get; set; }
}
As I see from your code, you have attached partially initialized object user to context.Users. As a result when UserManager gets control: AddToRole, it tries to update Database. And you'll have lot empty or null fields in the current users row.
You can fix doing any of the following (both will help):
instead of
ApplicationUser user = new ApplicationUser() use user = UserManager.FindById(UserId)
after assigning values from viewmodel EntityFramework will take care of modified fields.
use another context when dealing with roles
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
I have a Web API and I want my AutoHubUser to be used instead of the default one (IdentityUser). But I keep getting internal server error when I try to save it. What needs to be changed?
public class AutoHubUser : IdentityUser
{
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
[DataType(DataType.Date)]
[Display(Name = "Birth Date")]
public DateTime BirthDate { get; set; }
[Required]
public Gender Gender { get; set; }
public virtual ICollection<Auto> Autos { get; set; }
public virtual ICollection<UserMilestone> UserMilestones { get; set; }
public virtual ICollection<Friendship> Friendships { get; set; }
}
The account controller
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityUser user = new AutoHubUser
{
UserName = model.UserName,
Email = model.Email,
BirthDate = model.BirthDate,
Gender = model.Gender
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
//IdentityResult result = await new UserManager<AutoHubUser>().CreateAsync(user, model.Password);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
I also modified the RegisterBindingModel
public class RegisterBindingModel
{
[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; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime BirthDate { get; set; }
[Required]
public Gender Gender { get; set; }
}
What else needs to be modified? It must be in the UserManager's CreateAsync method. But I haven't been able to find an example online. Thanks!