MVC.net EF validation when using View Models - asp.net-mvc

I'm working on an MVC application and i'm trying to implement some validation. I've strucuture the site to use EF for storage and a set of view models with automapper.
I want to add some validation which i'm sure would work if i added it to the View Models however i'm assuming it would be better to put validation in with the EF model so if in the future i create another interface the same validation would also apply.
First of is this the correct approach and second how do i get MVC to actually test the validation before saving the object. Currently it just skips my EF validation.
The address model is auto generated so i created this partial class to add the validation:
public partial class Address : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(this.AddressLine1) &&
!string.IsNullOrWhiteSpace(this.AddressLine2) &&
!string.IsNullOrWhiteSpace(this.AddressLine3) &&
!string.IsNullOrWhiteSpace(this.Town) &&
!string.IsNullOrWhiteSpace(this.City) &&
!string.IsNullOrWhiteSpace(this.County) &&
!string.IsNullOrWhiteSpace(this.Postcode))
yield return new ValidationResult("Address cannot be blank.");
}
}
This is my view model class with the display names changed
public class AddressVM
{
public int? ID { get; set; }
[Display(Name = "Address line 1")]
public string AddressLine1 { get; set; }
[Display(Name = "Address line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name = "Town")]
public string Town { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "County")]
public string County { get; set; }
[Display(Name = "Postcode")]
public string PostCode { get; set; }
}
This is my controller
public ActionResult AddAddress(AddressVM vm)
{
IncidentAddress theAddress = Mapper.Map<AddressVM, Address>(vm);
if (ModelState.IsValid)
{
UOW.Addresses.Add(theAddress);
UOW.Save();
}
return PartialView("AddressVM-edit", vm);
}

if (ModelState.IsValid)
This will always be true for your object, as it will look for validity of your model, which is AddressVM (you receive that from view so this is your model) and does not have any validators. ModelState does not know that you have mapped this object to some other which implements validation. You need to run validation on your other object manually and add validation errors to ModelState.
If you want to have this separated, you can implement IValidatableObject on AddressVM, and internally perform validation by creating a instance of Address, mapping it from AddressVM (this) and returning result of it's Validate method. You also can expose the same constructed Address object as a property and use it to perform entity operation.
Example of AddressVM:
public class AddressVM : IValidatableObject
{
public int? ID { get; set; }
[Display(Name = "Address line 1")]
public string AddressLine1 { get; set; }
[Display(Name = "Address line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name = "Town")]
public string Town { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "County")]
public string County { get; set; }
[Display(Name = "Postcode")]
public string PostCode { get; set; }
//// I added this and interface in class definition:
public IncidentAddress GetIncidentAddress()
{
return Mapper.Map<AddressVM, Address>(this);
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return this.GetIncidentAddress().Validate(validationContext);
}
}
This way your logic stays in your business object, and your viewmodel uses it without having copy of it or some other dependency.

The Address class and AddressVm are not bound to each other in your case - AutoMapper does not do validation stuff, it just copies values. So you do not get ModelState populated and validations performed.
There're two workarounds i'm thinking of
Define the validations on AddressVm. If ModelState.IsValid, then map AddressVm to Address and save.
You do not need AddressVm at all. Change Action signature to expect Address parameter. That way, ModelState.IsValid will be automatically populated by validation system (Not the best solution).
Ideally, ViewModels should be defined for specific scenarios. In your case, I would define AddAddressModel, use it only for adding addresses and define only the properties needed to create address. Then, define validations on AddAddressModel and use mapper to map ViewModel to Address instance (So, I prefer first solution, plus defining specific model).
If you need reusable validator classes, you could check out FluentValidation. It has good support of asp.net-mvc too.

Related

Create Submit Error: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details

I am getting an error
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
when trying to submit on a simple create page. Below is the controller code and model. I can not figure out what the issue is.
All fields except ID are nullable in SQL. I know that the issue is coming from the fields resolution and technician - if I put them on the create form (which they are not on it now as I do not want them filled out) the submit works fine Any ideas?
Thanks,
EB
Controller:
public ActionResult Create()
{
HelpDesk b1 = new HelpDesk();
return View(b1);
}
[HttpPost]
public ActionResult Create(HelpDesk model)
{
db.HelpDesks.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
Model:
public int ID { get; set; }
[DisplayName("Requested By")]
public string RequestedBy { get; set; }
[Required(ErrorMessage = "Requested By Required.")]
public string Request { get; set; }
[Required(ErrorMessage = "Request Required.")]
public string Resolution { get; set; }
[DisplayName("Assigned To")]
public string Technician { get; set; }
public string Status { get; set; }
public string CreatedBy { get; set; }
public string ModfiedBy { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
All fields except ID is nullable in SQL.
That's not what you told Entity Framework, which is what's throwing the error. (And, as the error indicates, you should really check the EntityValidationErrors property on the exception, or an inner exception, for specific information about the error.) You told Entity Framework that these fields are required:
[Required(ErrorMessage = "Requested By Required.")]
public string Request { get; set; }
[Required(ErrorMessage = "Request Required.")]
public string Resolution { get; set; }
(It looks like you may have mixed up some of the property attributes, judging by the messages on them.)
I know that the issue is coming from the fields resolution and technician - if I put them on the create form (which they are not on it now as I do not want them filled out) the submit works fine
Sounds like that's the problem then. Resolution is marked as required, and you're not including it. Either include it or don't make it required.

Prevent to use default model data annotations in ViewModel

I started working on my first serious MVC project for school unfortunately without defining the data annotations to the model first (so did not set "required" annotation, limit to size of attribute names etc.). I work with a viewmodel, and after adding the annotations the model is causing my ViewModel state to get invalid when posting the form.
It seems like it's the email required that is causing the issue. It is not used on viewmodel and in the form and it seems the viewmodel expects it will get it. Is there a way the form to stop demanding this field by setting some limitation in viewmodel (or controller). I would really prefer not to change the structure of the application (if I start from the scratch I would probably do this a bit different, but not much time is left to finalize the project)
Customer (Model)
public Class Customer(){
public int Id { get; set; }
[Required(ErrorMessage = "Required")]
[StringLength(25, ErrorMessage = "Message"]
public string Name { get; set; }
public string Logo { get; set; }
//[Required(ErrorMessage = "Email required")]
//[Display(Name = "E-mail")]
//[RegularExpression(xxxx, ErrorMessage = "not correct")]
public string Email { get; set; }
public int UserId { get; set; }
}
ViewModel
public class CustomerEditViewModel
{
public Customer Customer { get; set; }
[FileTypes("jpg,jpeg,png")]
[FileSize(1024 * 1024, ErrorMessage = "Max x bytes")]
public HttpPostedFileBase File { get; set; }
}
You can remove errors from the modelstate in your controller, e.g.
this.ModelState[key].Errors.Clear();
where key is the bit to be cleared, so if it's email it's most likely -
this.ModelState["Customer.Email"].Errors.Clear();

Using a model for multiple purposes

I'm new to ASP.NET MVC using Entity Framework and I'm trying to create a simple login system. At the moment I have UserProfile model that I wish to model a login form off of.
UserProfile.cs
namespace MyProject.Areas.Admin.Models
{
public class UserProfile {
[Key]
public int UserID { get; set; }
[Required]
[Display(Name = "Username")]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public string Telephone { get; set; }
}
}
As my login form will only require a username and password, is it correct for me to create a separate model (for instance, a LoginModel with just those properties, or should I contine to use the UserProfile model?
It feels better for me to create a separate model to model the login submission, but then I run into the issues such as making them reference the same table?
Thanks
You should have only one Model (Domain model), but different ViewModel class.
The ViewModel will only have the properties (from the Model) needed for a certain View /Action.
To manage mapping between Model and ViewModel(s), you should look at Mapping solutions (like AutoMapper, ValueInjecter...)
It looks you should distinguish view model and domain model. Interestin discussion was here.

ASP.NET MVC - create a new Model or use a Entity framework class

I am developing a ASP.NET MVC 3 application, i am using entity framework code first in order to create the classes of my app, and i also have a repository in order to perform the operations on it, keeping clean the DBContext and the DBEntities definitions.
My doubt is about the render of the views and the way where a edit model is saved.
If I have this entity that represent a user stored in my DB:
//Entity:
public class User
{
[Key]
public int IdUser { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
And i want to show a View with the FirstName, LastName, Email and NewPassword, ConfirmPasword and CurrentPassword, in order to let the user change his data, typing the CurrentPassword to confirm the changes, so my doubt is, fieds like ConfirmPasword and CurrentPassword aren´t in my entity so i need to create a new model for this View and the copy the information that i want from my new Model to my database entity in order to save it? Like:
public class UpdateUserModel
{
[Required]
[Display(Name = "Name")]
public string FirstName{ get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName{ get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Not valid email")]
public string Email { get; set; }
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPasword{ get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm the New Pasword")]
[Compare("NewPasword", ErrorMessage = "Password doesn´t mach.")]
public string ConfirmPasword{ get; set; }
[Required(ErrorMessage = "Need to specify the current password to save changes")]
[DataType(DataType.Password)]
[Display(Name = "Current Password")]
public string CurrentPassword { get; set; }
}
and in the controller i made:
public ActionResult UpdateUser(UpdateUserModel model)
{
User u = (User)Membership.GetUser();
u.FirstName = model.FirstName;
u.LastName = model.LastName;
u.Email = model.Email;
if (!String.IsNullOrEmpty(model.NewPassword))
{
u.Password = FormsAuthentication.HashPasswordForStoringInConfigFile(model.NewPassword.Trim(), "md5");
}
repository.UpdateUser(u);
return View();
}
There are any way of doing this having a controller like:
public ActionResult UpdateUser(User u)
{
repository.UpdateUser(u);
return View();
}
Because if i have that, how i can add the field like, ConfirmPassword or CurrentPassword in order to made the validation for this specific View.
If I were you, I wouldn't use domain model in my presentation layer. I would create a view model (another class) which will be very similar to my domain model. I would then use auto-mapping tool to map from my domain model to the view model.
This is a very common scenario, so if you Google for "view and domain" models you should find everything you need.
public class User {
[Key]
public int IdUser { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class UpdateUserViewModel {
// Original fields
public string Password { get; set; }
public string PasswordConfirmation { get; set;
}
You could then configure auto-mapper to remove your boiler plate code:
public ActionResult ShowUser()
{
var domainModel = new User(); // I'm assuming that properties are set somewhere
var viewModel = new UserViewModel();
Autommaper.Map(domainModel, viewModel);
return View(viewModel);
}
This is very rough, but hopefully you get an idea.
Update 1: **
As i understood is better to create a new model for each view and then map it into the entity
It's not just better, it provides better separation of concerns, makes your code easily testable. Just by looking at the name of the class, I can see its purpose UpdateUserViewModel, RegisterUserViewModel etc).
Original fields, in this class is supposed to be the Metadata with the validation and that stuff isn't?
By original fields I mean:
public class UserViewModel{
public string UserName { get; set; }
public string FirstName { get; set; }
}
These fields are already in your User class, so I saved my time by not typing them in again.
This will be change my model from MVC to MVVM or not beacuse i still have a controller?
I believe what I've suggested is still an MVC pattern, rather than MVVM.
About the Automaper, are you using github.com/AutoMapper/AutoMapper?
Automapper is something that I have used. There are few tools out there and they do pretty much the same thing. Try out few and find one that suits your requirements the most.
Good luck.
Usually I use areas for different parts of my project, as an aside of where to put this extra code.
Pretty much you are going to add to your model folder a viewmodel.cs class. Inside this class will hold your definitions for how the data will be modelled in the view. These viewmodels will reflect the parts of the entity you wish the user to interact with. The interaction will be done in the controllers via [HttpGet] where you pass in the view model to be interacted with, and [HttpPost] where you send the model back and then map it to an entity.
ViewModels.cs:
public class UserViewModel
{
public string UserName { get; set; }
}
SomeController:
public ActionResult getView()
{
var uvm = new UserViewModel();
return View(uvm);
}
View getView.cshtml:
#model project.namespace.UserViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.UserName)
<input type="submit" value="New User Name" />
}
Back in controller:
[HttpPost]
public ActionResult getView(UserViewModel model)
{
var entity = new ActualEntity();
entity.username = model.UserName;
//more mapping
//commit changes somewhere
return RedirectToAction("getView");
}

MVC 3 validation not working for complex model

I have a UserFormModel which contains a UserModel which has a set of properties with the [Required] attribute set. I have read that MVC 3 out of the box will validate models within models by default. However when I submit an empty form in my view passing back a UserFormModel containing an empty UserModel the ModelState.IsValid is always true.
I have tried sending just the UserModel back to my controller and that validates ok. It just seem to be when I am working with complex models that it does not validate.
I have also tried it with the [Required] attribute on the User property within the UserFormModel (which I believe is not required for default behaviour to work) but still no validation takes place.
Any ideas on this one would be much appreciated.
public class UserFormModel
{
public UserModel User;
public IEnumerable<SelectListItem> Roles { get; set; }
}
public class UserModel : ModelBase
{
[Required]
public string UserName { get; set; }
public string Title { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
[HttpPost]
public ActionResult Create(UserFormModel userFormModel)
{
if (ModelState.IsValid)
{
// Do Something
}
}
You should use properties not fields. So instead of:
public UserModel User;
you should have:
public UserModel User { get; set; }
The reason for this is that the default model binder works only with properties.
I believe that validation only goes one model property deep in the model. For example if you have the following model
public class Product
{
public int ProductId { get; set; }
[Required]
public int ProductName { get; set; }
[Required]
public decimal Price { get; set; }
}
public class ProductViewModel
{
[Required]
public Product Product { get; set; }
}
The validation against the product object in the view model will work, the validation against the product class will not with one caveat. If the Product class is a POCO class used in the entity framework code first method, the validation will work against the database. Validation against a view model will only work one deep in my experience.

Resources