I'm using ASP.NET MVC3 and I have the following class:
public class AddressMetadata
{
public string State { get; set; }
public string City { get; set; }
public string Street { get; set; }
}
Also I have the following edit model:
[Display(Name = "First Address")]
public Address FirstAddress { get; set; }
[Display(Name = "Second Address")]
public Address SecondAddress { get; set; }
And I have to create all fields from FirstAddress as required, but the other ones from SecondAddress not.
How can I do that without creating new class for second address? I know that I can use [Required] directive in AddressMetadata class, but how can I divide those rules between FirstAddress and SecondAddress?
I guess something like this will work, not 100% sure, I've to try it myself. (I'm actually starting with EditorTemplates)
Add [Required] just to FirstAddress in the EditModel.
Add [Required] to all properties of the Adress class.
Write a TemplateEditor for Address class.
The TemplateEditor will have Address as its model and perform validation on that using Address class annotations, while the View will validate according to the EditModel annotations.
Please forgive me for my bad English.
EDIT: was forgetting about this: in the view render the EditorTemplate via
#Html.EditorFor (m => m.FirstAddress)
Related
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();
This question is similar to this link:
ViewModel to display partial information
I am hoping for an example however. I've created my model, it looks like this:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace GuestListTemplate.Models
{
public class Guest
{
public int ID { get; set; }
[Required]
[Display(Name = "First Name:")]
public string FirstName { get; set; }
[Display(Name = "Last Name:")]
public string LastName { get; set; }
[DataType(DataType.EmailAddress)]
[Display(Name = "e-Mail:")]
public string eMail { get; set; }
[DataType(DataType.PhoneNumber)]
[Display(Name = "Phone #:")]
public string phone { get; set; }
public Boolean OptIn { get; set; }
[Display(Name = "Guest Last Name:")]
public string GuestLastName { get; set; }
[Display(Name = "Guest First Name:")]
public string GuestFirstName { get; set; }
[DataType(DataType.Currency)]
public decimal Donation { get; set; }
public int? Attended { get; set; }
}
}
All of this information is not neccesary for some views. I want to make a viewmodel to display only the first name, last name, donation and perhaps join the First and Last name. I am trying to do something like this but it doesn't work:
namespace GuestListTemplate.ModelViewModels
{
public class GuestViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public decimal Donation {get; set;}
[Display(Name = "Full Name")]
public string FullName
{
get { return FirstName + " " + LastName;}
}
}
}
How do I tell MVC to use the GUEST model as the source of data for this viewmodel, or how do I bind them together? (The purpose for this is I thought it might help performance of my application, Will it actually help performance??). I understand how to use Viewmodels to display data from multiple tables and pull in ALL data but in this case I want to pull only certain data from one table. Last, Is this better accomplished in the controller?
I hope this question makes sense. Would it be better to use a LINQ query such as
var Guests = db.Guest.Include(g => g.firstname, g.lastname, g.donation);
return (Guests.ToList());
(I understand the above command might not be exact syntax bbut you get the idea). If this is a better way to do it can someone demonstrate a basic way of doing this with the entire viewmodel code. I am pretty good with LINQ, so if someone offers a basic example I should be able to make it work for my purposes.
EDIT:
This worked for me (as well the interfaces outlined in the answer below):
ViewModel is the same as above.
Here is what I do in the controller:
var viewmodel = db.Guests.Select(g => new GuestListIndexData
{
ID = g.ID,
FirstName = g.FirstName,
LastName = g.LastName,
Donation = g.Donation,
Attended = g.Attended
}).OrderBy(g => g.FirstName);
return View(viewmodel.ToList());
My view is essentially the same as what I listed above as well. This works, but i'm not sure how much faster performance I am actually getting out of it so I probably won't use it until I am working with very large databases.
I was able to achieve this with interfaces, such as this:
public interface INameEntity
{
string FirstName { get; set; }
string LastName { get; set; }
}
And add this to Guest:
public class Guest : INameEntity
And use INameEntity in the partial:
#model INameEntity
#Html.TextBoxFor(i => i.FirstName)
This is the technique I use and it works great if Guest is your direct model in the parent view. If there is a parent model class, it's doable but a little more complex setup.
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; }
}
I have a Person model and a student model. The student model has 2 FKs of PersonIDs; one for student and the other for parent.
My view looks like this:
#Html.EditorFor(m => m.student.Person.FirstName)
#Html.EditorFor(m => m.student.Person.DOB)
#Html.EditorFor(m => m.student.Father.FirstName)
The models would look like this:
public partial class Person
{
public int PersonID { get; set; }
[Required]
[PlaceHolder("First Name")]
public string FirstName { get; set; }
[PlaceHolder("Birth Date")]
public Nullable<System.DateTime> DOB { get; set; }
}
public partial class Student
{
public int Student_PersonID { get; set; }
public int Parent_PersonID { get; set; }
}
I want the DOB to be required field for the student but not for the parent. If I add [Required] attribute to the DOB element, then it requires it for both. Is there a way I can set a require a field on the view? or is there a way in the model or using validation attribute to do this?
fyi... i am using EF database first approach
thanks
I would suggest having the view model match the fields that are displayed in the view. If later a field is to be removed from the view, then it will also be removed from the domain model.
In this case, if your view is to display the following fields:
StudentFirstName
StudentDOB
ParentFirstName
ParentDOB
Then I would suggest having the following view:
public class PersonViewModel
{
public int StudentPersonID { get; set; }
[Required]
public string StudentFirstName { get; set; }
[Required]
public DateTime StudentDOB { get; set; }
public int ParentPersonID { get; set; }
[Required]
public string ParentFirstName { get; set; }
public DateTime ParentDOB { get; set; }
}
Or if instead you have 2 seperate views displaying:
StudentFirstName
StudentDOB
AND displaying:
ParentFirstName
ParentDOB
Then I would suggest having 2 seperate view models:
public class StudentViewModel
{
public int StudentPersonID { get; set; }
[Required]
public string StudentFirstName { get; set; }
[Required]
public DateTime StudentDOB { get; set; }
}
public class ParentViewModel
{
public int ParentPersonID { get; set; }
[Required]
public string ParentFirstName { get; set; }
public DateTime ParentDOB { get; set; }
}
Using the view models in this way will allow you to use the [Required] data annotations for the fields that require them rather than trying to create a workaround. Note that the view models are not to be confused with the domain models and therefore this data would then need to be mapped to the domain model.
Hope this helps.
If your application is a simple application you may not need to create a seperate business logic layer and most books only present MVC with simple models which may be fine. However, if you search around you will find other examples where developers recommend having a view model seperate from a business model such as this
I would also recommend reading Wrox Professional Enterprise .Net 2009 where chapters 7 & 8 give great examples of the business layer with discussions of the Transaction Script pattern, Active Record pattern and Domain Model pattern.
One way is to make a PersonRequired class that inherits from Person. Add a metadata class to PersonRequired so you have PersonRequiredMetaData and in that specific that the inherited DOB field is required. You would need to manually copy the values between the Person and PersonRequired classes or use AutoMapper. I hope there is a better answer than this!
Another option is to use FluentValidation that would let you do the validation separate from the model (doesn't use data annotations). I wonder if some people are using data annotations for database requirements and fluent validation for programmatic requirements.
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.