I use Entity Framework, so almost all my models are in fact the database. However, sometimes I want to pass to the view a simpler object that is not necessary a database model with all the properties.
For example, I have this model :
int ID
string name
string email
string country
string username
string password
When I pass a model to the login view, I only need the username and password. Thus, I thought about creating a simplified model with only username and password.
My questions :
1) What's the best way to standardize this ? Any best practice ?
2) Can I use inheritance to avoid repeating properties like username / password ?
3) Where I put this non-database model ... in wich cs file ?
4) And what about the name of this simplified model ?
Thank you !
Yeah you can use ViewModels.
These are regular classes that contain properties that you need for your view. And these properties could be a subset of a model or the combination of multiple models.
For the login view you could create a LoginViewModel
public class LoginViewModel
{
public int UserId {get; set;}
[Required]
public string Username {get; set;} //included required attribute for username
public string Password {get; set;}
}
You can now project your viewmodel from your database query (or any other source).
ViewModels are very helpful as in many cases the views typically don't map one to one with models or may be need annotations that you are not willing to add to your models directly.
So many great uses.
Related
Is a model meant for a specific view only?
Or multiple views can reuse the same model?
For instance, a typical website has registration and login feature.
Is it necessary to do two different models (RegistrationViewModel & LoginViewModel) for this registration and login views respectively?
Or I can have a model, let's say named as UserAccountViewModel
public class UserAccountViewModel
{
public string email {get; set;}
public string password {get; set;}
public string confirmPassword {get; set;}
}
And both registration and login views can use the use the same model (UserAccountViewModel)?
If two ways are do-able, then which is a better practice?
I need to capture three different types of information from a new user who is registering for the first time in an MVC 3 app (using EF code first). Ideally on the one page (which will have three tabs)
User info
Extended user info
Benefits chosen by user
The register method of the account controller should populate the user table when it fires. I need some advice on how best to capture the other data. DO I create a UserDetails model for the additional data? If so is it possible to update this from the same page? (which would be the Account/Register page. Do I need to do something in the account controller or will the relationship between the models be enough?
Are there any good examples about that would explain this? I tried the MVC Contoso University one but couldn't see if I could do this.
Any advice very welcome :)
Thanks
You can create a view model that combines all the fields you need from the other models, and strongly type your view to that. Once the user submits the info back to the controller, you'll process each property appropriately.
public class RegisterViewModel()
{
//Userinfo
public string UserName {get; set;}
...
//Extended user info
public string FirstName {get; set;}
public string LastName{get; set;}
...
//Benefits
public string BenefitName {get; set;}
...
}
and then
[HttpPost]
public ActionResult Register (RegisterViewModel viewModel)
{
//grab the user info from the view model and process it
viewModel.UserName...
//grab the extended info and process it
viewModel.FirstName...
//grab the benefit info and process it
viewModel.BenefitName...
}
A single view can only have one model. You can put all the information you need there. It's the simplest solution. The model of your view doesn't have to map to classes you're using elsewhere so you can have your User info, Extended user info and Benefits chosen by user all in one model.
Alternatively, you could make this a two- or three-step process, but you said you don't want that.
One common recommended practice in asp.net mvc is that you should not send your business models to your views.. instead you should create viewmodels specific to each view.
When that is done and you call the ModelState.IsValid method in your controller you are effectively checking the validity of the viewmodel but not the business object.
What is the conventional approach to dealing with this?
public class Person
{
public int ID {get; set;};
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
public virtual ICollection<Exam> Exams {get; set;}
}
public class PersonFormViewModel
{
public int ID {get; set;};
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
}
This is exactly what I have right now but Im not sure if the [Required] attribute should appear on both models or just the ViewModel or just the Business Model.
Any tips on this issue are appreciatedd.
More links to support my claim that it is a common good practice to always use view models.
How to add validation to my POCO(template) classes
http://blogs.msdn.com/b/simonince/archive/2010/01/26/view-models-in-asp-net-mvc.aspx
My preference is to do input validation on the view models, and business validation on the domain models.
In other words, any data annotations such as required fields, length validation, regex, etc should be done on your view models, and added to the model state when error occurs.
And you'll probably have business/domain rules that rely on more than just a "form", so you should do that either in the domain models (execute the validation after they're mapped back), or with a service layer.
All our models have a method called "Validate", which we call in the services prior to persisting. They throw custom exceptions if they fail business validation, which gets caught by the controller and also added to the model state.
May not be everyone's cup of tea, but it's consistent.
Example of business validation, as requested:
Here's an example of a domain model we have, which represents a generic "Post" (question, photo, video, etc):
public abstract class Post
{
// .. fields, properties, domain logic, etc
public void Validate()
{
if (!this.GeospatialIdentity.IsValidForThisTypeOfPost())
throw new DomainException(this, BusinessException.PostNotValidForThisSpatial.);
}
}
You see there, I am checking against business rules, and throwing custom exceptions. DomainException is our base, and we have many derived implementations. We have an enum called BusinessException, which contains values for all our exceptions. We use extension methods on the enum to provide the resource-based error message.
This is not simply a field on the model I'm checking, e.g "All posts must have a subject", because although that is part of the domain, it's input validation first and foremost, and thus is handled via the data annotations on the view model.
Now, the controller:
[HttpPost]
public ActionResult Create(QuestionViewModel viewModel)
{
if (!ModelState.IsValid)
return View(viewModel);
try
{
// Map to ViewModel
var model = Mapper.Map<QuestionViewModel,Question>(viewModel);
// Save.
postService.Save(model); // generic Save method, constraint: "where TPost: Post, new()".
// Commit.
unitOfWork.Commit();
// P-R-G
return RedirectToAction("Index", new { id = model.PostId });
}
catch (Exception exc)
{
var typedExc = exc as DomainException;
if (typedExc != null)
{
// Internationalised, user-friendly domain exception, so we can show
ModelState.AddModelError("Error", typedExc.BusinessError.ToDescription());
}
else
{
// Could be anything, e.g database exception - so show generic msg.
ModelState.AddModelError("Error", "Sorry, an error occured saving the Post. Support has been notified. Please try again later.");
}
}
return View(viewModel);
}
So, by the time we get to the "Save" method on the service, the model has passed input validation. Then the Save method calls post.Validate(), invoking business rules.
If an exception is raised, the controller catches it and displays the message. If it gets pass the Save method and another error occurs (90% of the time, it's Entity Framework, for example), we show a generic error message.
As I said, not for everyone, but this works well for our team. We have a clear separation of presentation and domain validation, and a consistent flow of control from the raw HTTP POST, to the redirect after success.
The MetaData "buddy" class is exactly what this is for. The validation is created once but can be used on both the model and the viewmodel classes:
public class PersonMetaData
{
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
}
[MetadataType(typeof(PersonMetaData))]
public class Person
{
public string Name {get; set;}
public string LastName {get; set;}
}
[MetadataType(typeof(PersonMetaData))]
public class PersonFormViewModel
{
public string Name {get; set;}
public string LastName {get; set;}
}
Great answer by RPM1984, and nice code sample.
My view is still that using Variant 2 from the start is a pragmatic balance between productivity and structure, but you always have to be willing to move to Variant 3 in some cases, so I advocate a mix of the two approaches where logical. Patterns & Practices however recommend always doing Variant 3 as it is the ideal separation of concerns etc, and it avoids you using two approaches in the same solution which some people don't like and many customers I work with pick Variant 3 and run with that for all models.
I think the key is what RPM1984 said - reusing your business entities inside your View Models is useful for the sake of reusing the validation, but do bear in mind that often your business logic needs to do different validation too (e.g. checking the record doesn't already exist). If you use Variant 3 it empowers you to focus your View Model validation purely on the needs of your views (at the expense of a little extra effort) but you will always need some kind of business logic validation too.
Given that you always pass viewmodels to your view and your domain models are not exposed to the end user through views then i don't see any need for validating domain model itself. For example if you receive PersonViewModel from view through form post you will transform it to Person Model (perhaps through automapper etc.) only if PersonViewModel is valid itself. So i believe that input validations should stay with the view models because they are the one that are bound to input.
Typically your ViewModel will contain a reference to your Model - a ViewModel is only necessary if you need to display additional information in your view that isn't available in your Model, there is no need to replicate the data already present.
I'm using ASP.NET 4 and MVC3.
Often, I find that I need a ViewModel to display information for my Model. For example, take the following model
class Profile
{
public int UserID { get; set; }
public string Name { get; set; }
public DateTime DOB { get; set; }
}
There is a requirement to hide the UserID, but to show the UserName, so often time for models that are similar to the one above, I have to come up with a ViewModel with just the UserID changed to UserName:
class ProfileViewModel
{
public string UserName { get; set; }
public string Name { get; set; }
public DateTime DOB { get; set; }
}
Are there any ways?
Until recently I always passed my models to my action methods as I also thought that creating viewModels with the same property names was duplication (its not). This caused me a lot of pain. I have now been re-educated and almost always use viewModels exclusively in my action methods (of course there will always be situations were it is fine to pass the model directly to the action method).
Have a read of this post which is the one that converted me to using viewModels. This will tell you the following:
The difference between models and viewModels
When each should be used.
How to avoid some security issues with the default model binder.
On top of the information in the linked post you should also consider things such as validation. I had a model that implemented the IValidateableObject interface to ensure the entity was in a valid state before being saved to the database.
In my ASP.NET application I wanted to create a multi-step form that allowed the user to enter the information over a number of pages. The problem I had here was that ASP.NET also uses the IValidatableObject interface during the model binding process.
If you are only allowing the user to enter a subset of the information required for the entity, the model binder will only be able to fill in the information that was given. Depending on how complex your validation is, this can result in the ModelState being marked as invalid as the entire entity is not valid.
The way I got around this was to have a viewModel representing each step each with its own validation. This way you are only validating the properties at each step. Once you get to the final step and everything is valid, I create an appropriate entity using the information given by the user. This entity will only have database-level validation checks performed upon it (field lengths etc.)
My suggestion is not to avoid viewModels but to understand why they are used and embrace them.
No, there isn't, once a member is public, it's public. Now, if the UserID property was internal, then you wouldn't have that problem.
However, one of the aims of MVVM here is to encapsulate logic regarding the interaction of the model and the view. Even if you have the view model and model in separate assemblies and make the UserID property internal, you should still have a view model; if changes come down the line where more functionality is required than simply binding to the model, you are prepared.
Direct access to the model is always a no no.
Additionally, if you really wanted, you could always use T4 templates to auto-generate the code for you (you could use Code DOM on the original CS file) to output your view models for you.
I usually have multiple ViewModels per model - the tradeoff you have to make comes down to this:
Are you comfortable coupling business logic (data annotations, display information, etc...) with your (persistence) models?
Are you comfortable doing all of the hide / display business logic purely within the View and not use the Controller + scaffolding to make those decisions for you?
The downside of creating all of those ViewModels of course is sub-class explosion, but the right way to think about it is in terms of the questions I listed IMHO.
I am just at the brink of going "Ah HA!" when it comes to coding Domain Driven Design. The question is How and where to define a MVC ActionMethod parameter class that involves an entity, a value object, and a few extra fields? The entity and value object classes are defined in my repository.
Do I:
Create a custom class in the repository implementing the other classes (to get the properties of all in a single class), and add a few more properties for the extra fields?
Create the entity / value object poco classes in a repository and create a composite class referencing these objects in my controller class, then use this as the ActionMethod parameter type?
Something else?
The request form simply collects a few Customer class fields, a mailing address, and a few form specifics like how they found us. The content is not important, only that it holds information from multiple pocos.
I know MVC will match the fields of the posted form to the properties of the Poco in the ActionMethod parameter like the following:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult RequestCatalog()
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult RequestCatalog(Customer customer)
So customer.firstName is bound to firstName in the posted form automatically.
I have the following:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult RequestCatalog(string fname, string lname, string address,
string city, string state, string zip, string phone, string howTheyFoundUs)
But want to have something like:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult RequestCatalog( RequestForm requestForm)
Any thoughts?
First of all you must realize that the Domain Model must be defined independently of the run-time framework (MVC). Imagine that in the future you must be able to expose the Domain Model as a WCF service, or a WPF/Silverlight rich client, or a batch job, or something else...
This means that all modeling must be unconstrained by technical details. How you store your data and the specifics of how the Domain Objects are consumed must be (largely) ignored at this stage of the design process.
You must always ask yourself: Does this class make sense as a part of the pure domain?
In you specific case, most of your input sounds like it belongs on a Customer class with a structure similar to this
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public Address Address { get; set; }
}
and the Address class is defined similarly.
Now the question you need to ask yourself (or any Domain Experts you are working with): Does the howTheyFoundUs belong on the Customer class? Or is is a Domain concept in its own right? Or is it really just an application-specific piece of data that doesn't have anything to do with the Domain Model at all? The answer to such questions will guide how you end up modeling your input.
As an aside, the Phone property above looks dubious to me, but is a pretty good example on how implementation details (in this case ASP.NET MVC) may leak into the model.
A phone number should really be a Value Object in its own right (see this blog post for a related treatment of this subject), but the default ModelBinder of ASP.NET requres a primite type. A better option would be to implement a custom ModelBinder that can parse a phone number into a proper object.
There is nothing to stop you using Request.Form in your action method.
E.g.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult RequestCatalog(Customer customer)
{
var howTheyFoundUs = Request.Form["howTheyFoundUs"];
// ...
}