Should I use data annotations in domain model too? ASP.NET MVC - asp.net-mvc

I am a newbie in ASP.NET MVC, and something made me confused.
I am creating a login/registration web-app, and when I came to confirm password, I was a bit confused. I surely don't want confirm password column in my database. So for that reason, I use ViewModel. And I use data annotations for validation in my ViewModel. So there is no need to write any validation code in my Domain Model.
But when Entity Framework creates a table from my Domain Model object, from where will it get information for example about how many characters should username take? If I used data annotations in my domain model, I would write MaxLength or something.
Should I validate data in domain model too?

You client side validation can be taken care of using Data Annotations on your View Model and include jQuery validation script in your View.
So in your View Model, you can set minimum password length restriction like this:
using System.ComponentModel.DataAnnotations;
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
Of course, this is only for client side validation, for server side validation, you have to validate the data in your controller, but i don't believe you have to use data annotation on your domain model.
So in your controller, you can validate the data passed through like this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterViewModel model)
{
//checks for data passed through, if somehow people bypasses client side validation
if (ModelState.IsValid)
{
//continue
}
//validation failed, return to view
return View(model);
}
ModelState.IsValid indicates if it was possible to bind the incoming values from the request to the model correctly and whether any explicitly specified validation rules were broken during the model binding process. ---- what does this do : ModelState.IsValid

Related

What is the cycle of server side validation

I have a model of name Student
public class Student
{
[Required]
public string Name { get; set; }
public string Number { get; set; }
public string Email { get; set; }
}
My question is that, does form is submitted and then validations are checked or there is another mechanism that post hidden form values to the server side validations ?
Generally in web development, validation occurs both client side and server side.
You want to scrub the forms for any unwanted data or characters before they are submitted, and you want to check them / scrub them again server side to ensure nothing is being passed that is unwanted.

Different validation on insert and edit

I'm using DataAnotation for validation and i need disable it of in some cases.
F.E. on create i need user insert password and confirmation, but for edit it can stay empty and not changed.
I have this model:
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[DisplayName("Re-enter Password")]
[Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
public string PasswordControl { get; set; }
Enought vould be disable required on password on edit.
AFAIK, there are two ways, either will work.
Use different model for edit and insert. I prefer and use this one in my application. It's easy and future proof(Edit and insert models and rules may be quite different).
Customize a ValidationAttribute and override IsValid method. Use some context such as IsEdit field of your model. It can be used since MVC3. See the "Model Validation Improvements" part of this article http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

MVC4 Web API server side validation display

Ref. this Microsoft's official video:
http://www.asp.net/web-api/videos/getting-started/custom-validation
I downloaded the code and run it. It's fine.
Then, I remove all the client validation attributes(data-val-*) from the html file. It didn't work fine. I could not see the validation messages on the web page.
My question is how to regular the server side validation messages and how to display them as client-side validation.
Why would you delete the validation attributes? That's exactly what gets you the validation messages. To change the validation tests, you need to set the appropriate validation attributes on the model properties, e.g.,
[Required]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
As described in this post on ASP.NET MVC 4 Model Validation.

How can I reuse Model Metadata for custom View Models?

I'm working on an ASP.NET MVC 2 project with some business entities that have metadata dataannotations attributes applied to them (Validation attributes, Display attributes, etc.).
Something like:
//User entity
public class User
{
[DisplayName("Vorname")]
[Required(ErrorMessage = "Vorname fehlt")]
[StringLength(MaxNameLength, ErrorMessage = "Vorname ist zu lang")]
public string FirstName { get; set; }
[DisplayName("Nachname")]
[Required(ErrorMessage = "Nachnamefehlt")]
[StringLength(MaxNameLength, ErrorMessage = "Nachname ist zu lang")]
public string LastName { get; set; }
[Required]
public string Password{ get; set; }
}
Using the metadata from different views is no problem, as long as I am using my business entities as viewmodels or as part of a viewmodel like this:
//custom viewmodel with a user entity
public class CustomViewModel
{
public User{get;set;}
//some more properties...
}
However, sometimes I need to code a view for editing some, but not all fields of an entity. For those fields I want to reuse the metadata already specified in my user entity. The other fields should be ignored. I'm talking about custom view models like this:
[MetadataType(typeof(User))]
public class UserNameViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
//no password on purpose, the user should only
//edit his first and last name in this view
}
That's where I am running into problems. The custom view model above leads to an exception when the view is generated, because it has no password property.
The associated metadata type for type
'Zeiterfassung.Models.ViewModels.Users.UserNameViewModel+UserModel'
contains the following unknown
properties or fields: Password. Please make sure
that the names of these members match
the names of the properties on the
main type.
Also, even if this exception did not occur, I expect to get into even more trouble with model validation on form submit because Password is marked as required in my business entity.
I can think of several workarounds, but none seem really ideal. In any case I can't change the database layout so that the password field would be in a separate entity in my example above.
How would you handle this scenario?
The only recommendation I could give you is to have view models specific to each view and have only the necessary properties and validation attributes on those view models. Don't worry if you repeat some validation attributes and properties on your view models. That's what they are meant for: reflect the logic of a given view.

ASP.NET MVC: How can I explain an invalid type violation to an end-user with Html.ValidationSummary?

Serious n00b warning here; please take mercy!
So I finished the Nerd Dinner MVC Tutorial and I'm now in the process of converting a VB.NET application to ASP.NET MVC using the Nerd Dinner program as a sort of rough template.
I am using the "IsValid / GetRuleViolations()" pattern to identify invalid user input or values that violate business rules. I am using LINQ to SQL and am taking advantage of the "OnValidate()" hook that allows me to run the validation and throw an application exception upon trying to save changes to the database via the CustomerRepository class.
Anyway, everything works well, except that by the time the form values reach my validation method invalid types have already been converted to a default or existing value. (I have a "StreetNumber" property that is an integer, though I imagine this would be a problem for DateTime or any other non-strings as well.)
Now, I am guessing that the UpdateModel() method throws an exception and then alters the value because the Html.ValidationMessage is displayed next to the StreetNumber field but my validation method never sees the original input. There are two problems with this:
While the Html.ValidationMessage does signal that something is wrong, there is no corresponding entry in the Html.ValidationSummary. If I could even get the exception message to show up there indicating an invalid cast or something that would be better than nothing.
My validation method which resides in my Customer partial class never sees the original user input so I do not know if the problem is a missing entry or an invalid type. I can't figure out how I can keep my validation logic nice and neat in one place and still get access to the form values.
I could of course write some logic in the View that processes the user input, however that seems like the exact opposite of what I should be doing with MVC.
Do I need a new validation pattern or is there some way to pass the original form values to my model class for processing?
CustomerController Code
// POST: /Customers/Edit/[id]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
Customer customer = customerRepository.GetCustomer(id);
try
{
UpdateModel(customer);
customerRepository.Save();
return RedirectToAction("Details", new { id = customer.AccountID });
}
catch
{
foreach (var issue in customer.GetRuleViolations())
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(customer);
}
This would be a good starting point: Scott Gu - ASP.NET MVC 2: Model Validation
But I would suggest that it's bad practice to databind stuff to your domain objects.
My personal approach would be to use POCO presentation models sprinkled with DataAnnotations validation attributes to do validation (this makes it easy to hook up client side validation later on). You can also create you own ValidationAttributes to hook into the databinding validation.
If it's valid after databinding to your POCO object, pass it a service that does your more complex business validation. After that validation passes, pass it to another service (or the same service) that transfers the values to your domain object and then saves it.
I'm not familiar with the Linq to SQL GetRuleViolations pattern, so feel free to replace one of my steps with that pattern where it fits.
I'll try my best to explain it here.
POCO presentation model:
public class EditCustomerForm
{
[DisplayName("First name")]
[Required(ErrorMessage = "First name is required")]
[StringLength(60, ErrorMessage = "First name cannot exceed 60 characters.")]
public string FirstName { get; set; }
[DisplayName("Last name")]
[Required(ErrorMessage = "Last name is required")]
[StringLength(60, ErrorMessage = "Last name cannot exceed 60 characters.")]
public string LastName { get; set; }
[Required(ErrorMessage = "Email is required")]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}" +
#"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
#".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$",
ErrorMessage = "Email appears to be invalid.")]
public string Email { get; set; }
}
Controller logic
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, EditCustomerForm editCustomerForm)
{
var editCustomerForm = CustomerService.GetEditCustomerForm(id);
return View(editCustomerForm);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, EditCustomerForm editCustomerForm)
{
try
{
if (Page.IsValid)
{
//Complex business validation that validation attributes can't handle
//If there is an error, I get this method to throw an exception that has
//the errors in it in the form of a IDictionary<string, string>
CustomerService.ValidateEditCustomerForm(editCustomerForm, id);
//If the above method hasn't thrown an exception, we can save it
//In this method you should map the editCustomerForm back to your Cusomter domain model
CustomerService.SaveCustomer(editCustomerForm, id)
//Now we can redirect
return RedirectToAction("Details", new { id = customer.AccountID });
}
//ServiceLayerException is a custom exception thrown by
//CustomerService.ValidateEditCusotmerForm or possibly .SaveCustomer
catch (ServiceLayerException ex)
{
foreach (var kvp in ex.Errors)
ModelState.AddModelError(kvp.Key, kvp.Value);
}
catch (Exception ex) //General catch
{
ModelState.AddModelError("*", "There was an error trying to save the customer, please try again.");
}
return View(editCustomerForm);
}
Clear as mud? If you need more clarification, just let me know :-)
Again, this is just my approach. You could possibly just follow the article by Scott Gu I linked to first and then go from there.
HTHs,
Charles

Resources