Where to put validation annotations ViewModel or Domain object? - asp.net-mvc

My Question is
As I am passing UserCreateViewModel from my Create Controller that means my Validation(ModelState.IsValid) will work only
on UserCreateViewModel if Annotation are defined on it. But I can not define DataAnnotation on each of my ViewModels because that will be alot of work.
Instead I want to put it on User domain model. So how do I fix Create method to fix as my Annotation work and mapper too without adding more code to controller.
//My Controller Create Method
[HttpPost]
public ActionResult Create(UserCreateViewModel user)
{
if (ModelState.IsValid)
{
var createUser = new User();
Mapper.Map(user, createUser);
_repository.Add(createUser);
return RedirectToAction("Details", new { id = createUser.UserId });
}
return View("Edit", user);
}
//UserCreateViewModel -> Create Specific view model
public class UserCreateViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
//User -> Domain Object
[MetadataType(typeof(User.UserValidation))]
public partial class User
{
private class UserValidation
{
[Required(ErrorMessage = "UserName is required.")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "{0} is between {1} to {2}")]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
public string UserName { get; set; }
[Required(ErrorMessage = "Password is required.")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "{0} is between {1} to {2}")]
public string Password { get; set; }
}
}

Validation should be put at least on the view model because this is what you receive as user input. As far as validation on the model is concerned you could add it as well but as long as you are passing a view model to your POST action (which is exactly what you should do) the validation on the model will be ignored. Of course that's not a problem here because a model is something that might be reused on other applications as well which don't use view models and this way your model is guaranteed to be valid. As far as ASP.NET MVC is concerned this step is not necessary.

Related

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();

MVC 4 Inserting Model Data into a database using Repository Framework

Here is my ListView Model which moreorless corresponds with a datbase table I have built called Comment.
public int EntryId { get; set; }
public DateTime Posted { get; set; }
public string AuthorIp { get; set; }
[Required(ErrorMessage = " A Name is required *")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[StringLength(160, MinimumLength = 2, ErrorMessage = "Must be between 2 & 160 characters in length.")]
public string AuthorName { get; set; }
[Required(ErrorMessage = "Email address required *")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[StringLength(160, MinimumLength = 2, ErrorMessage = "Must be between 2 & 160 characters in length *")]
[EmailValidation(ErrorMessage = "Must be valid email *")]
public string AuthorEmail { get; set; }
[Required(ErrorMessage = " A Message is required *")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[StringLength(4000, MinimumLength = 2, ErrorMessage = "Must be between 2 & 4000 characters in length *")]
public string Body { get; set; }
public ListView(IBlogRepository blogRepository)
{
Posts = blogRepository.Posts();
}
public ListView(){ }`
I need to get some of the properties into my Comment table. Iam using or at least attempting to use the IRepository Framework. It goes like this...
public interface IBlogRepository : IDisposable
{
IList<Entry> Posts();
void InsertComment(Comment comment);
void Save();
}
Here is my inherit class...
public class BlogRepository : IBlogRepository, IDisposable
{
private BlogDataDataContext _dataContext;
public BlogRepository() { _dataContext = new BlogDataDataContext(); }
// #region IBlogRepository Members
public void InsertComment(Comment comment)
{
_dataContext.Comments.InsertOnSubmit(comment);
}
public void Save()
{
_dataContext.SubmitChanges();
}
So I call above InsertComment from my BlogController like this.
[HttpPost]
public ActionResult BlogPost(ListView pModel)
{
pModel.Posted = DateTime.Now;
_repository.InsertComment( // Cannot pass pModel as is not Comment type.
return RedirectToAction("BlogPost");
}
So my problem is that my ListView pModel is passed in but its not of type Comment so I cant Insert it properly. I need my ListView model because it contains extra validation rules and a couple of constructors. Anyone have an idea of where I am going wrong.
Do I need to create a Model which directly mirrors the datbase table I am asdding too. Then where do I move my Constructors and other validation rules? It feels like I need two models. One on top of the other. I'm so close to understanding this now.
Thanks in advance.
If you use ASP.NET MVC your model should be an exact "copy" of your table.
If you want some other informations to pass to your view you can use viewmodels.
In your example why don't you do something like:
[HttpPost]
public ActionResult BlogPost(ListView pModel)
{
pModel.Posted = DateTime.Now;
foreach (Comment comment in ListView.Posts)
{
_repository.InsertComment(comment);
}
return RedirectToAction("BlogPost");
}
I think your approach looks good overall, but you'll need some way of obtaining that current Comment object to pass into your repository. Whether you create another object to house both objects, or refactor your code, I would recommend looking at the following article:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
You could use AutoMapper to map properties from your ViewModel to your Comments business model in your controller:
var comment = Mapper.Map<ListView, Comment>(pModel);
_repository.InsertComment(comment);
This is common practice when working with ViewModels or DTO's.

Should I have each ViewModel for each entity in my ASP MVC project?

I have read many by many solution but i can't understand deeply about what or when i use View Model?
For example, when i have a Register form for User to register, i want to hava an field Confirm Password, but i don't think should add it into the User entity. So i have this ViewModel:
public class RegisterViewModel
{
public User User { get; set; }
public IEnumerable<SelectListItem> City { get; set; }
public IEnumerable<SelectListItem> Ward { get; set; }
[Required(ErrorMessage = "Bạn chưa nhập lại mật khẩu.")]
[StringLength(100, ErrorMessage = "Mật khẩu phải có ít nhất {2} ký tự.", MinimumLength = 6)]
[DataType(DataType.Password)]
[System.Web.Mvc.Compare("User.Password", ErrorMessage = "Mật khẩu không khớp.")]
public string ConfimPass { get; set; }
}
So after read this link How to properly implement "Confirm Password" in ASP.NET MVC 3? . I don't know why they should replace the Password field which is already in User entity. I'm using unobstrusive client validation so it does work if i use this Model View. In my View, i must use m=> m.User.Username but not m=>m.Username, etc... Because of this, my validation such as compare password, or just remote validation not work well with the name in my View like m=>m.User.Username. What is wrong with my structure or my Model View in my thinking?
There is no single rule and you need to stay pragmatic, having said that ViewModel and a Model (or Domain Model) are 2 different things. No you don't pollute your entities by placing properties that don't belong to them. The idea is that your UI should be interchangeable and your domain should not in any way depend on it. The dependencies should be inverted. Maybe tomorrow you'd switch (or extend) your UI layer to WPF (for example) ? Where your current ViewModels (with their attributes) wouldn't make much sense.
In your case, yes you should be creating a view model and keep everything relevant to the view in them, after which you map/pass values back to your domain model.
I hope I'm making sense, let me know if you need clarifications.
In your case I'd probably create a flattened RegisterViewModel that would include only the information needed to register a user, for example:
public class RegisterViewModel
{
[Required]
public string DisplayName { get; set; }
[Required]
public string FirstName { get; set; }
// etc ...
public IEnumerable<SelectListItem> City { get; set; }
public IEnumerable<SelectListItem> Ward { get; set; }
[Required(ErrorMessage = "Bạn chưa nhập lại mật khẩu.")]
[StringLength(100, ErrorMessage = "Mật khẩu phải có ít nhất {2} ký tự.", MinimumLength = 6)]
[DataType(DataType.Password)]
[System.Web.Mvc.Compare("User.Password", ErrorMessage = "Mật khẩu không khớp.")]
public string ConfimPass { get; set; }
}

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");
}

asp mvc validation specific to controllers action?

Can the built in ASP MVC validation be made to behave differently for different actions of a same controller ? For example I have a user controller and it has actions like create, edit and other actions. So in model user the attribute Username is being validated for its uniqueness. If there is an user present with the same username, it throws and error username already present. So using the same validator for edit action throws an error "username already present" while editing an user. Can anybody tell me if there is a way to do solve this problem? I am pasting my validator code for reference.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace Models
{
[MetadataType(typeof(AdmiUserMetadata))]
public partial class AdminUser
{
public class AdmiUserMetadata
{
[Required(ErrorMessage = "Required Field")]
public string Id { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
[Username(ErrorMessage = "Username already taken")]
public string Username { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
public string Password { get; set; }
[Required(ErrorMessage = "Required Field")]
public string Name { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("^[a-z0-9_\\+-]+(\\.[a-z0-9_\\+-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,4})$", ErrorMessage ="Invalid E-mail ID")]
public string Email { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("(Active|Disabled)", ErrorMessage = "Select the status of User")]
public string Status { get; set; }
[Required(ErrorMessage = "Required Field")]
[RegularExpression("^[1-9]", ErrorMessage = "Select the group of User")]
public string Group { get; set; }
}
}
public class UsernameAttribute : ValidationAttribute
{
IUserRepository _repository = new UserRepository();
public override bool IsValid(object value)
{
if (value == null)
return true;
if (_repository.IsUsernamePresent((string)value))
{
return false;
}
return true;
}
}
}
What you are validating is a business rule.
No two users can have the same username.
I would have a User service that enforces this rule on creation/edit. Attributes are best suited for input validation. (eg Is the integer non-negative? A valid email address? etc)
I don't see how this can be done if a class has attributes that determines validation. This obviously works for most projects, but for me this is also not working out.
If you need to attach different sets of validation rules check out http://fluentvalidation.codeplex.com/. I tried it and liked it.
It doesn't handle client validation. I dropped that because I have ajax calls in most parts and that feels a bit like client validation.

Resources