MVC view model decoupling - asp.net-mvc

I am at the very first step of MVC.
In my first example I have a Model like so:
public class GuestResponse
{
[Required(ErrorMessage = "Please enter your name")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter your email")]
[RegularExpression(".+\\#.+\\..+", ErrorMessage = "Please enter a valid email address")]
public string Email { get; set; }
public string Phone { get; set; }
[Required(ErrorMessage = "Please specify whether you'll attend")]
public bool? WillAttend { get; set; }
}
the Controller:
public class HomeController : Controller
{
public ViewResult Index()
{
ViewData["greeting"] = (DateTime.Now.Hour < 12 ? "Good morning" : "Good afternoon");
return View();
}
[HttpGet]
public ViewResult RsvpForm()
{
return this.View();
}
[HttpPost]
public ViewResult RsvpForm(GuestResponse guestResp)
{
if (ModelState.IsValid)
{
return this.View("Thanks", guestResp);
}
else
{
return this.View();
}
}
}
and a View:
<body>
<div>
<h1>
Thank you,
<%: Model.Name %>.</h1>
<p>
<% if (Model.WillAttend == true)
{ %>
It's great that you're coming. The drinks are already in
the fridge!
<% }
else
{ %>
Sorry to hear you can't make it, but thanks for letting
us know.
<% } %>
</p>
</div>
What seems strange to me, is that the View is thightly coupled with the Model: it uses code like Model.WillAttend ecc... So what happen if in a future time the Model changes? I should have to change all the snippets inside this particular View (but possibly also in many other views...)
Is this tight-coupling or am I missing the point here?
EDIT
In the book the Author points out that this useful mechanism is
Model Binding
Given that the method [public ViewResult RsvpForm(GuestResponse guestResp)] is being
invoked via an HTTP request, and that GuestResponse is a .NET type that is totally unknown to HTTP,
how can an HTTP request possibly supply a GuestResponse instance? The answer is model binding, an
extremely useful feature of ASP.NET MVC whereby incoming data is automatically parsed and used to
populate action method parameters by matching incoming key/value pairs with the names of properties
on the desired .NET type.
This powerful, customizable mechanism eliminates much of the humdrum plumbing associated
with handling HTTP requests, letting you work primarily in terms of strongly typed .NET objects rather
than low-level fiddling with Request.Form[] and Request.QueryString[] dictionaries, as is often
necessary in Web Forms. Because the input controls defined in RsvpForm.aspx render with names
corresponding to the names of properties on GuestResponse, the framework will supply to your action
method a GuestResponse instance already fully populated with whatever data the user entered into the
form.
My two cents: but so, this mechanism give you the advantage of strongly typed classes inside the View (which, however, is really useful thanks to Visual Studio Intellisense feature, otherwise in my opinion also the Request.Form[] syntax would fit the same). On the other hand, you are tightly coupling the View with the Model, so that every tiny change to the Model will result in a cascade of changes on linked Views.

Is this tight coupling or am I missing the point here
Yes it is tight coupling and the point you maybe missing is - this isn't the M in MVC, this is a different type of model called a view model and it's supposed to be tailored for a specific view so it makes sense that it's coupled.
It's generally known as MVVM, a derivation of MVC which introduces the notion of view models to de-couple your actual model from the view.

Related

Exactly why is it a bad idea to use a strongly typed _layout.cshtml in MVC?

For reasons given below, strongly typing the layout seems a logical approach.
But I am scared to ignore the warnings of so many programmers so much more skilled an experienced than myself.
Why do so many recommend avoiding this approach?
Brief Breakdown of Arguments For / Against
I am planning to use a model like this as a base class for my application's ViewModels.
public class LayoutViewModel
{
public string CanonicalURL { get; set; }
public string PageTitle { get; set; }
public string MetaTitle { get; set; }
public string Description { get; set; }
public string OGImage { get; set; }
public string OGType { get; set; }
}
This question generated some sensible sounding arguments against doing so, for example:
"layout is a partial used by all of your views. Specifying a model
there would add restriction to every view on your site to also have
that model."
"you are avoiding one "bad practice" (dynamic typing of ViewBag), but
trying to replace it with another bad practice (tying your layout to
model data). Layouts should not rely on data.."
Ant P suggested: "You should delegate the parts of your layout that
"need a model" to a separate controller using partial views and
RenderAction"
But on reflection, I don't find them convincing:
PageTitle, MetaTitle, Description and OG data are required for
every full page view, so I feel it's proper that the ViewModel should contain
that data.
Why should I avoid "tying my layout to model data"? Is not every MVC view tied to the model it is based on?
ViewBag is inconvenient and error prone
The only disadvantage I see is that layout changes may require changes to the base model also. But this seems minor compared to the advantages a strongly typed _layout.cshtml. And realistically, it's unlikely that the properties above will change any time soon.

Where do you put your validation in asp.net mvc 3?

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.

Advice on structuring MVC ViewModel Classes (Parent with many children)

I'm writing a message board webpage. The page consists of a Topic item, then a list of Response and a form to add an additional response.
Im struggling to structure my page and viewdata classes in such a way that they are clean and allow me to take advantage for editor templates and validation attributes.
Currently I have one page to do all the above, and Im thinking my viewdata class will eventually look something like this:
public class TopicViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string Title { get; set; }
[ValidateNonEmpty("Please enter some text")]
public string TopicBody { get; set; }
public IList<TopicResponseViewsData> Responses { get; set; }
public TopicResponseViewsData NewResponse { get; set; }
}
public class TopicResponseViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string ResponseText{ get; set; }
}
My page is typed to a TopicViewsData, it just seems ugly that I have to have NewResponse property just so the page can have access to the validation attributes on TopicResponseViewsData. Is there a nicer way to do this?
Sounds like you are headed towards a massive and complex view, not to mention the issues you are already seeing with your model structuring. Rather than making trade offs to make what you have work I have a few recommendations on your overall view model design.
I tend to separate my models into ViewModels and FormModels. ViewModels are for displaying data and FormModels are for taking user input. Not only does this provide a clear designation of function it generally allows me to keep my FormModel properties typed to primitives, strings, and dates in addition to providing a single place for applying validation logic. While, in my ViewModels I am afforded the flexibility to use complex property types and do not have to worry about validation logic.
To make things even easier I follow Jimmy Bogard's suggestion that you should have only one view per model. By not mixing and matching models I have found my models stay focused and my views do not turn into spaghetti. To keep things tidy I name my models similarly to the Controller and View they are tied to. I might end up with a few extra models, but it is a small price to pay for a cleaner design.
I think that the Body property in the TopicViewsData model is redundant with the NewResponse property.
So your view is working with responses where each response has a body. So:
public class TopicResponseViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string Body { get; set; }
}
So far so good. Next you said that you have a list of responses to show and a new response to add, so:
public class TopicViewsData
{
public IList<TopicResponseViewsData> Responses { get; set; }
public TopicResponseViewsData NewResponse { get; set; }
}
For the moment, given your description that's all I see necessary in the view model. At least model reflects your scenario description.

Asp.Net MVC2 TekPub Starter Site methodology question

Ok I've just ran into this and I was only supposed to be checking my emails however I've ended up watching this (and not far off subscribing to TekPub).
http://tekpub.com/production/starter
Now this app is a great starting point, but it raises one issue for me and the development process I've been shown to follow (rightly or wrongly). There is no conversion from the LinqToSql object when passing data to the view. Are there any negitives to this?
The main one I can see is with validation, does this cause issues when using MVC's built in validation as this is somthing we use extensivly. Because we are using the built in objects generated by LinqToSql how would one go about adding validation, like
[Required(ErrorMessage="Name is Required")]
public string Name {get;set;}
Interested to understand the benifits of this methodology and any negitives that, should we take it on, experiance through the development process.
Should this be taken as a guide and we should be using ViewModels? If so should we always use them even in simple cases? And how/where in the application logic does the Entity get converted to a ViewModel?
With entity objects, you could use buddy classes, whereby you create a second class which acts as a metadata provider for your entity. For instance, with a Customer entity generated by Linq-to-Sql, I could create a buddy class like so:
[MetadataType(typeof(CustomerMeta))]
partial class Customer {
}
public class CustomerMeta {
[DisplayName("Forename", Required(ErrorMessage = "Forename is required.")]
public string Forename { get; set;}
}
Entities are generated as partial classes so you can add your own code to them.
Alternatively, you could forego pushing your entity types to your views and create specific models based around the functionality required, for instance I would typically have a User entity, but when I need to create a User, I have something called a CreateUserSpec model:
public class CreateUserSpec
{
[DisplayName("Forename")]
public string Forename { get; set; }
}
Which has a subset of the properties of the User, only those required to create a User. This is the model I would pass to my view, and repopulate from the form data. For instance:
public class AccountController
{
public ActionResult Register() {
return View(new CreateUserSpec());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(CreateUserSpec spec) {
if (!ModelState.IsValid) {
return View(spec);
}
var user = UserFactory.CreateUser(spec);
// Redirect to authorisation page?
}
}

Conditionally validating portions of an ASP.NET MVC Model with DataAnnotations?

I have certain panels on my page that are hidden under certain circumstances.
For instance I might have a 'billing address' and 'shipping address' and I dont want to validate 'shipping address' if a 'ShippingSameAsBilling' checkbox is checked.
I am trying to use the new DataAnnotations capabilities of ASP.NET MVC 2 (preview 1) to achieve this.
I need to prevent validation of the 'shipping address' when it is not displayed and need to find the way way to achieve this. I am talking mainly server side as opposed to by using jquery.
How can I achieve this? I have had several ideas, related to custom model binding but my current best solution is below. Any feedback on this method?
For the CheckoutModel I am using this approach (most fields hidden):
[ModelBinder(typeof(CheckoutModelBinder))]
public class CheckoutModel : ShoppingCartModel
{
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
public bool ShipToBillingAddress { get; set; }
}
public class Address
{
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; }
[Required(ErrorMessage = "First name is required")]
public string FirstName { get; set; }
[Required()]
public string LastName { get; set; }
[Required()]
public string Address1 { get; set; }
}
The custom model binder removes all ModelState errors for fields beginning with 'ShippingAddress' if it finds any. Then 'TryUpdateModel()' will return true.
public class CheckoutModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
base.OnModelUpdated(controllerContext, bindingContext);
var model = (CheckoutModel)bindingContext.Model;
// if user specified Shipping and Billing are the same then
// remove all ModelState errors for ShippingAddress
if (model.ShipToBillingAddress)
{
var keys = bindingContext.ModelState.Where(x => x.Key.StartsWith("ShippingAddress")).Select(x => x.Key).ToList();
foreach (var key in keys)
{
bindingContext.ModelState.Remove(key);
}
}
}
}
Any better solutions?
http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html
I can see your predicament. I'm looking for other validation solutions also with regard to complex validation rules that might apply to more than one property on a given model object or even many properties from different model objects in a object graph (if your unlucky enough to be validating linked objects like this).
The limitation of the IDataErrorInfo interface is that a model object satisfies the valid state simply when none of the properties have errors. This is to say that a valid object is one where all of it's properties are also valid. However, i may have a situation where if property A, B and C are valid - then the whole object is valid.. but also if property A is not valid but B and C are, then the object satisfies validity. I simply have no way of describing this condition/rule with the IDataErrorInfo interface / DataAnnotations attributes.
So i found this delegate approach. Now many of the helpful advancements in MVC didn't exist at the time of writing this article but the core concept should help you. Rather than using attributes to define the validation conditions of an object we create delegate functions that validate more complex requirements and because they're delegated we can re-use them. Sure it's more work, but the use of delegates means that we should be able to write validation rule code once and store all the validation rules in the one place (maybe service layer) and (the kool bit) even use the MVC 2 DefaultModelBinder to invoke the validation automatically (without heaps of checking in our controller actions - like Scott's blog says we can do with DataAnnotations. Refer to the last paragraph before the 'Strongly Typed UI Helpers' heading)!
I'm sure you can beef the approach suggested in the above article up a little with anonymous delegates like Func<T> or Predicate<T> and writing custom code blocks for the validation rules will enable cross-property conditions (for example the condition you referred to where if your ShippingSameAsBilling property is true then you can ignore more rules for the shipping address, etc).
DataAnnotations serves to make simple validation rules on objects really easy with very little code. But as your requirements develop you will need to validate on more complex rules. The new virtual methods in the MVC2 model binder should continue to provide us with ways of integrating our future validation inventions into the MVC framework.
Make sure the fields you don't want validated are not posted to the action. We only validate the fields that were actually posted.
Edit: (by questioner)
This behavior has changed in MVC2 RC2 :
Default validation system validates
entire model The default validation
system in ASP.NET MVC 1.0 and in
previews of ASP.NET MVC 2 prior to RC
2 validated only model properties that
were posted to the server. In ASP.NET
MVC 2, the new behavior is that all
model properties are validated when
the model is validated, regardless of
whether a new value was posted.
Applications that depend on the
ASP.NET MVC 1.0 behavior may require
changes. For more information about
this change, see the entry Input
Validation vs. Model Validation in
ASP.NET MVC on Brad Wilson’s blog.
For the more complex cases I moved away from simple DataAnnotations to the following: Validation with visitors and extension methods.
If you want to make use of your DataAnnotations you would replace something like the following:
public IEnumerable<ErrorInfo> BrokenRules (Payment payment)
{
// snip...
if (string.IsNullOrEmpty (payment.CCName))
{
yield return new ErrorInfo ("CCName", "Credit card name is required");
}
}
with a method to validate a property by name via DataAnnotations (which I don't have atm).
I created a partial model binder that only validates the keys that were submitted. For security reasons (if I was going to take this a step farther) I'd create a data annotation attribute that marks which fields are allowed to be excluded from a model. Then, OnModelUpdated check field attributes to ensure there is no undesired underposting going on.
public class PartialModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
// default model binding to get errors
base.OnModelUpdated(controllerContext, bindingContext);
// remove errors from filds not posted
// TODO: include request files
var postedKeys = controllerContext.HttpContext.Request.Form.AllKeys;
var unpostedKeysWithErrors = bindingContext.ModelState
.Where(i => !postedKeys.Contains(i.Key))
.Select(i=> i.Key).ToList();
foreach (var key in unpostedKeysWithErrors)
{
bindingContext.ModelState.Remove(key);
}
}
}
This isn't related to DataAnnotations but have you looked at the Fluent Validation project? It gives you fine grain control over your validation and if you have object-to-object validation an aggregate object of the two objects will get you going.
Also it seems to have been build with MVC in mind but it also has its own "runtime" so that you can use it in other .NET applications as well which is another bonus in my book.

Resources