database.SaveChanges() throws EntityValidationException - asp.net-mvc

New to MVC. When I try to add a user to the database using Entity Framework Database First I get this exception:
An exception of type 'System.Data.Entity.Validation.DbEntityValidationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
This is the code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(RegisterViewModel account)
{
if (ModelState.IsValid)
{
using (db)
{
bool duplicate = db.Users.Any(a => a.UserName == account.UserName);
if (duplicate)
{
ModelState.AddModelError("", "Username already exists in database!");
}
else
{
db.Users.Add(new StoreFront.Models.User { UserName = account.UserName, Password = account.Password, EmailAddress = account.EmailAddress, IsAdmin = false, DateCreated = DateTime.Now });
db.SaveChanges();
ModelState.Clear();
ModelState.AddModelError("RegisterSuccess", "Successfully registered!");
}
}
}
return View();
}
I have validation in my RegisterViewModel for all fields, and when I debug, IsValid = true, otherwise it wouldn't run anyway. Any help would be greatly appreciated...I have been struggling with this for a while.
P.S. Yes the password is currently being stored as a string, this is just a test project that won't be used in the real world.
EDIT: Added Models:
User Model from database:
public partial class User
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public User()
{
this.Addresses = new HashSet<Address>();
this.Orders = new HashSet<Order>();
this.ShoppingCarts = new HashSet<ShoppingCart>();
}
public int UserID { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string EmailAddress { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public Nullable<System.DateTime> DateCreated { get; set; }
public string CreatedBy { get; set; }
public Nullable<System.DateTime> DateModified { get; set; }
public string ModifiedBy { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Address> Addresses { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Order> Orders { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ShoppingCart> ShoppingCarts { get; set; }
}
Partial Model to add ConfirmPassword:
namespace StoreFront.Models
{
[MetadataType(typeof(RegisterViewModel))]
public partial class User
{
[DisplayName("Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords must match")]
public string ConfirmPassword { get; set; }
}
}
RegisterViewModel:
public class RegisterViewModel
{
public int UserID { get; set; }
[DisplayName("Username")]
[Required(ErrorMessage = "Username is required")]
public string UserName { get; set; }
[DisplayName("Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
[DisplayName("Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords must match")]
public string ConfirmPassword { get; set; }
[DisplayName("Email")]
[Required(ErrorMessage = "Email is required")]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$",
ErrorMessage = "Please enter a valid email")]
public string EmailAddress { get; set; }
public Nullable<bool> IsAdmin { get; set; }
public Nullable<System.DateTime> DateCreated { get; set; }
}

Fix: When I looked up a tutorial about MVC it asked em to create a partial class and a meta class. That was for code first I believe, which basically made it a new field that my database didn't have a spot for, and I am using database first. So I removed the deleted the partial class for User and it stopped making ConfirmPassword an actual field in the database.
Don't know the real works of it, or if what I said makes sense, but I hope this helps someone eventually.

Remove
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$",
ErrorMessage = "Please enter a valid email")]
from RegisterViewModel

Related

MVC Validation - Issue checking DB records for matching values before posting

Hi everyone I cant seem to figure out how to check a record before creating a new post in MVC
When a user creates a "SuperMember" I want it to check IF that "Character" has already been assigned that "membership" already aka a duplicate record. Each Character can have many memberships but not the same one.
Heres my code -
MODELS
public class Character
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CharacterID { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[Display(Name = "Hero Alias")]
public string HeroAlias { get; set; }
[Required]
[Display(Name = "Email Address")]
public string Email { get; set; }
public ICollection<SuperMembers> SuperMembers { get; set; }
}
public class Membership
{
[Key]
public int MembershipID { get; set; }
[Required]
[Display(Name = "Membership Name")]
public string MembershipName { get; set; }
[Required]
[Display(Name = "Membership Tyoe")]
public string Type { get; set; }
public ICollection<SuperMembers>SuperMembers { get; set; }
}
public class SuperMembers
{
[Key]
[Display(Name = "Membership Number")]
public int SuperMembersID { get; set; }
[Required]
[Display(Name = "Hero Alias")]
public int CharacterID { get; set; }
public Character Character { get; set; }
[Required]
[Display(Name = "Membership")]
public int MembershipID { get; set; }
public Membership Membership { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
[Required]
[Display(Name = "Account Balance")]
public decimal AccountBalance { get; set; }
}
Then the Controller POST Method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("SuperMembersID,CharacterID,MembershipID,AccountBalance")] SuperMembers superMembers)
{
if (ModelState.IsValid)
{
var IfAlreadyExists = dbo.SuperMembers.Where(x => x.CharacterID == CharacterID && x.MembershipID == MembershipName).FirstOrDefault();
if (IfAlreadyExists == null)
{
//POST
}
else
{
//Return ERROR "Sorry this Membership already exists"
}
_context.Add(superMembers);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["CharacterID"] = new SelectList(_context.Character, "CharacterID", "HeroAlias", superMembers.CharacterID);
ViewData["MembershipID"] = new SelectList(_context.Membership, "MembershipID", "MembershipName", superMembers.MembershipID);
return View(superMembers);
}
The dbo in "dbo.SuperMembers" is underlined too, im using a local db for this little practice project so not sure if thats also a factor?
Im fairly new to this, this is my first StackOverflow post so apologies if ive missed anything obvious - Would appreciate any pointers. Thanks

VS2019 ASP.NET(MVC) - How to Login with only Email and Password

I made a User object that contains 7 properties when you create a new user.
The problem is that when I want to login - I want to enter only Email and Password, and when the data goes to the UsersController to the Login function(POST, and GET) it doesn't process and skip the whole "if (ModelState.IsValid)" because it didn't get the rest of the data(because the function only gets the Email and Password and not the other values).
So my question is, How can I do that even if my model has 7 properties(attributes), only the Email and Password will be taken into consideration "if (ModelState.IsValid)" and log me in.
I hope I could manage to explain my problem, my English isn't that good I know that.
Screenshot
My User model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace ourProject.Models
{
public enum UserType
{
Guest,
Client,
rManager,
Admin
}
public class User
{
public int Id { get; set; }
[Required(ErrorMessage = "Please insert Email")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Please insert password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Full name")]
[Required(ErrorMessage = "Please insert name")]
public String Name { get; set; }
[Display(Name = "Phone number")]
[Required(ErrorMessage = "Please insert phone")]
[DataType(DataType.PhoneNumber)]
public String PhoneNumber { get; set; }
[Display(Name = "Birthday Date")]
public DateTime BirthdayDate { get; set; }
//one to one
public CreditCard CreditCard { get; set; }
public UserType Type { get; set; } = UserType.Guest;
}
}
My UsersController:
// GET: Users/Login
public IActionResult Login()
{
return View();
}
// POST: Users/Login
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([Bind("Id,Email,Password")] User user)
{
if (ModelState.IsValid)
{
var q = from u in _context.User
where u.Email == user.Email && u.Password == user.Password
select u;
if (q.Count() > 0)
{
//_context.Add(user);
//await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index), "Home");
}
else
{
ViewData["Error"] = "Username and/or password are incorrect.";
}
}
return View(user);
}
Screenshot
Try to separate User into 2 classes:
public class UserLogin
{
public int Id { get; set; }
[Required(ErrorMessage = "Please insert Email")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Please insert password")]
[DataType(DataType.Password)]
public string Password { get; set; }
}
public class User:UserLogin
{
[Display(Name = "Full name")]
[Required(ErrorMessage = "Please insert name")]
public String Name { get; set; }
[Display(Name = "Phone number")]
[Required(ErrorMessage = "Please insert phone")]
[DataType(DataType.PhoneNumber)]
public String PhoneNumber { get; set; }
[Display(Name = "Birthday Date")]
public DateTime BirthdayDate { get; set; }
//one to one
public CreditCard CreditCard { get; set; }
public UserType Type { get; set; } = UserType.Guest;
}
and action
public async Task<IActionResult> Login(UserLogin userLogin)
another way is to assign default values to required properties and create hidden fields in view
public IActionResult Login()
{
var model= new User{ Name="Unknown", PhoneNumber="123-123-4567" }
return View(model);
}
.....
//view
<input type="hidden" asp-for="#Model.Name" value=#Model.Name" />

MVC Entity Framework Error on Mapping Entity with [NotMapped] properties

I have define a Entity call Users...that is mapped to Users table with EF.
public partial class Users
{
public long User_id { get; set; }
[Required]
[StringLength(30, ErrorMessage = "LastName cannot be longer than 30 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(30, ErrorMessage = "Name cannot be longer than 30 characters.")]
public string Name { get; set; }
public int Country_id { get; set; }
public Nullable<int> State_id { get; set; }
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public System.DateTime CreationDate { get; set; }
public Nullable<System.DateTime> UpDateTime { get; set; }
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[Required]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Password cannot be longer than 9 characters.")]
public string Password { get; set; }
public int Rol_id { get; set; }
public byte[] Picture { get; set; }
public string CodArea { get; set; }
public string PhoneNumber { get; set; }
public virtual Ages Ages { get; set; }
public virtual Countries Countries { get; set; }
[NotMapped] // Does not effect with your database
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Confirm Password cannot be longer than 9 characters.")]
[Compare("Password")]
public virtual string ConfirmPassword { get; set; }
}
I use this entity to inherit from my Create View...
When I Update, I do not need a lot of this properties. I defined a a new Entity called UserEditView with this definition. I do not have Password and ConfirmPassword.
public partial class UserEditView
{
public long User_id { get; set; }
[Required]
[StringLength(30, ErrorMessage = "LastName cannot be longer than 30 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(30, ErrorMessage = "Name cannot be longer than 30 characters.")]
public string Name { get; set; }
public int Country_id { get; set; }
public Nullable<int> State_id { get; set; }
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public System.DateTime CreationDate { get; set; }
public Nullable<System.DateTime> UpDateTime { get; set; }
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[Required]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Password cannot be longer than 9 characters.")]
public byte[] Picture { get; set; }
public string CodArea { get; set; }
public string PhoneNumber { get; set; }
}
I defined a Mapper in my Global asax and Ignore those properties I dot not need.
cfg.CreateMap<Users, UserEditView>();
cfg.CreateMap<UserEditView, Users>()
.ForMember(x => x.CreationDate, opt => opt.Ignore())
.ForMember(x => x.Password, opt => opt.Ignore())
.ForMember(x => x.ConfirmPassword, opt => opt.Ignore())
.ForMember(x => x.Rol_id, opt => opt.Ignore());
}
When I Update, I mapp UserEditView to Users so I can call _db.SaveChanges() like this.
public async Task<ActionResult> Edit(UserEditView model, System.Web.HttpPostedFileBase image = null)
{
try
{
if (!ModelState.IsValid)
{
return View(model);
}
model.user.UpDateTime = DateTime.Now;
model.user.IP = Request.UserHostAddress;
model.user.Url = UserValidation.EncriptacionURL(model.user.Email);
var user = _db.Users.FirstOrDefault(p => p.User_id == model.user.User_id);
if (user == null)
{
return View(model);
}
Mapper.Map<UserEditView, Users>(model.user, user);
_db.Entry(user).State = System.Data.Entity.EntityState.Modified;
_db.SaveChanges();
}
}
in user I have the data I have in Users table. So ConfirmPassword is null because it is set as [NotMapped].
On _db.SaveChanges() I have an error because ConfirmPassword is null, so, when it is compared with Users Entity, it is compared whith Password property and failed.
How can I work when using [NotMapped] properties there is null?
Thanks
The problem I had in a Property ConfirmPassword I have add to my Users Entity with the Notation [NotMapped]. That case me a problem, when I do AutoMapper to Update Modified fields. Entity Validation ocurrs and ConfirmPassowrd is null, so, when validations ocurrs, I had an error on SaveChanges();.
I solved it adding
context.Configuration.ValidateOnSaveEnabled = false;

AutoMapper and required fields

I am trying to create a new record using only some of the fields in my Domain Model. I have created a ViewModel for this and am using AutoMapper.
My code is failing at the minute due to the required fields that I have on my Domain Model. I have added opt=>opt.Ignore() on the necessary field however, I am still having problems.
When I remove [Required] from the StaffPresent field the record is added to the DB.
In my Global.asax.cs
Mapper.CreateMap<CustomerSupportRecord, CustomerSupportRecordForCreation>();
Mapper.CreateMap<CustomerSupportRecordForCreation, CustomerSupportRecord>().ForMember(p=>p.StaffPresent, opt=>opt.Ignore());
Domain Model
public class CustomerSupportRecord
{
public int CustomerSupportRecordID { get; set; }
[Required]
public int CustomerID { get; set; }
[Required]
public string EmployeeID { get; set; }
[Required(ErrorMessage = "Please enter a Date")]
[DataType(DataType.Date)]
[Display(Name = "Date")]
public DateTime Date { get; set; }
[Required(ErrorMessage = "Please select an Arrival Time")]
[DataType(DataType.Time)]
[Display(Name = "Arrival")]
public DateTime ArrivalTime { get; set; }
[DataType(DataType.Time)]
[Display(Name = "Departure")]
public DateTime? DepartureTime { get; set; }
[Required(ErrorMessage = "Please select a Type")]
[Display(Name = "Type")]
public int CustomerSupportTypeID { get; set; }
[Required(ErrorMessage = "Please enter the staff who were present at the Feedback")]
[Display(Name = "Staff Present at Feedback")]
public string StaffPresent { get; set; }
[Display(Name = "Setting")]
public string ReflectionSetting { get; set; }
[Display(Name = "Advisor")]
public string ReflectionAdvisor { get; set; }
[Display(Name = "Notes")]
public string Notes { get; set; }
[Display(Name = "Comments")]
public string Comments { get; set; }
// Navigation Properties
public virtual Customer Customer { get; set; }
public virtual CustomerSupportType CustomerSupportType { get; set; }
public virtual Employee Employee { get; set; }
}
ViewModel
public class CustomerSupportRecordForCreation
{
public int CustomerSupportRecordID { get; set; }
public int CustomerID { get; set; }
public string EmployeeID { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Date")]
public DateTime Date { get; set; }
[DataType(DataType.Time)]
[Display(Name = "Arrival")]
public DateTime ArrivalTime { get; set; }
[Display(Name = "Type")]
public int CustomerSupportTypeID { get; set; }
[Display(Name = "Notes")]
public string Notes { get; set; }
}
And finally my Controller
//
// GET: /CustomerSupport/CustomerSupportRecord/Create
public ActionResult Create()
{
ViewBag.CustomerSupportTypeID = new SelectList(db.CustomerSupportType, "CustomerSupportTypeID", "CustomerSupportTypeName");
var model = new CustomerSupportRecordForCreation { CustomerID = 1, EmployeeID = "20213" };
return View("Create", model);
}
//
// POST: /CustomerSupport/CustomerSupportRecord/Create
[HttpPost]
public ActionResult Create(CustomerSupportRecordForCreation customersupportrecord)
{
if (ModelState.IsValid)
{
var newRecord = Mapper.Map<CustomerSupportRecordForCreation, CustomerSupportRecord>(customersupportrecord);
db.CustomerSupportRecord.Add(newRecord);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CustomerSupportTypeID = new SelectList(db.CustomerSupportType, "CustomerSupportTypeID", "CustomerSupportTypeName", customersupportrecord.CustomerSupportTypeID);
return View(customersupportrecord);
}
AutoMapper's configuration doesn't have anything to do with validation attributes, it just specifies the rules for how to map objects between each-other.
In your case, the mapping:
Mapper.CreateMap<CustomerSupportRecordForCreation, CustomerSupportRecord>().ForMember(p=>p.StaffPresent, opt=>opt.Ignore());
tells AutoMapper not to copy the StaffPresent property.
If your database model has a [Required] attribute, you'll still need to set that data elsewhere.
One thing to note based on your comment, it's a good idea to add validation to your ViewModels, as it prevents you from calling your database methods with improper data, gives you client side validation, and lets you enforce different constraints than your model might require.

Problems with editing using a custom model

I have this data model:
public class User
{
public long UserID { get; set; }
[Required(ErrorMessage = "User name is required.")]
[MaxLength(50, ErrorMessage = "User name cannot be longer than 50 characters.")]
public string UserName { get; set; }
[Email]
[Required(ErrorMessage = "Email is required.")]
[MaxLength(100, ErrorMessage = "Email cannot be longer than 100 characters.")]
public string Email { get; set; }
[Required(ErrorMessage = "Password is required.")]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[MaxLength(150, ErrorMessage = "Full name cannot be longer than 150 characters.")]
public string FullName { get; set; }
public int UserTypeID { get; set; }
public virtual UserType UserType { get; set; }
public virtual ICollection<Page> Pages { get; set; }
}
and I'm using this model to only edit some fields (password shouldn't be editable):
public class EditUserModel
{
public long UserID { get; set; }
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Email]
[Required(ErrorMessage = "Email is required.")]
[MaxLength(100, ErrorMessage = "Email cannot be longer than 100 characters.")]
public string Email { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Full name")]
[MaxLength(150, ErrorMessage = "Full name cannot be longer than 150 characters.")]
public string FullName { get; set; }
public int UserTypeID { get; set; }
public virtual UserType UserType { get; set; }
}
but I'm confused on how to pass the EditUserModel to my data context to update it. Sorry if seems elementary, but I'm really stumped.
This is the auto-generated edit action that I modified:
[IsAdministrator]
[HttpPost]
public ActionResult Edit(EditUserModel user)
{
if (ModelState.IsValid)
{
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserTypeID = new SelectList(db.UserTypes, "UserTypeId", "Name", user.UserTypeID);
return View(user);
}
This is the line I'm having trouble with:
db.Entry(user).State = EntityState.Modified;
The reason I created a custom class was to avoid exposing the password from the view.
This can't work because you're trying to save view model.
You could use AutoMapper to rewrite data from view model to your data model. After that you should be able to save changes.
User userModel = Mapper.Map<EditUserModel, User>(user);
userModel = // todo: get password from database
// todo: attach your model to context and save changes
I'm using Entity Framework Code First and that approach works great.

Resources