I'm using data annotations to check data that's being entered, but I'm stuck when it comes to more custom way to validate data.
I need to run queries against database to see if stuff exists there or not, and then report back to user if a "custom db-check error" appears, such as "The Companyname already exists"
How can I implement such a thing together with dataannotations?
I have all the queries done etc using linq and entity framework that comes with 3.5sp1
/M
Custom attributes that extend data annotations
You will have to write your own attributes that will do the validation of your object instance against data store.
Make sure your classes inherit System.ComponentModel.DataAnnotations.ValidationAttribute class:
public class MustNotExist: ValidationAttribute
{
...
}
Caution
I've run into a similar situation when I needed to validate that the object is unique within data store. But this kind of validation wasn't possible on the entity class itself, since it should only work for those entities that are being created but not when you return your entity from the data store already.
My solution was to have a separate interface, class and attribute.
public interface IExternalValidator ...
class DBUniqueValidator: IExternalValidator ...
class ValidateExternallyAttribute: FilterAttribute, IActionFilter
{
...
public ValidateExternallyAttribute(Type validatorType, Type entityType) ...
...
}
I was able to place my attribute on controller actions that get entity parameters. Filter action attribute then checks controller action parameters (it can easily access their types and values) and runs external validator against correct parameters (provided types in attribute definition) and populates ModelState errors when validation fails.
[ValidateExternally(typeof(DBUniqueValidator), typeof(User))]
public ActionResult RegisterUser(User newUser)
{
if (!this.ModelState.IsValid)
{
// act accordingly - probably return some error depending on model state errors
}
// register new user in data store
}
This way I was able to run external validation only on those actions that actually needed it, and this technique also helped my controller actions code to stay clean and short. All I had to do is to check if there are any model state errors.
Related
I have a multi-layered project. Layers are as follows :
Business
DataAccess
Entities
Core
MvcWebUI
I have a Category class in entity layer:
public class Category : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
I also have a CategoryValidator class in the business layer:
public class CategoryValidator : AbstractValidator<Category>
{
public CategoryValidator(IEnumerable<Category> categories)
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(50);
}
}
I have a class in the Core layer for validation.
public class ValidatorTool
{
public static void FluentValidate(IValidator validator, object entity)
{
var result = validator.Validate(entity);
if (result.Errors.Any())
throw new ValidationException(result.Errors);
}
}
I'm performing validation in the Business layer with the FluentValidate method.
But I got stuck when it came to the MvcWebUI layer. According to the FluentValidation documentation, I need to apply an attribute to the entity class as follows:
[Validator(typeof(PersonValidator))]
But since the business layer references the entity layer, I can't reach the CategoryValidator class in the entity layer. (circle reference)
How can I solve this problem?
Did I create the layers incorrectly?
Or should I define the entities as a model again in the Web layer?
Please help me.
Firstly, you probably shouldn't be exposing your entities directly in the UI so I'm going to recommend you create new models there and write validators specifically for them.
Assuming this is wired up correctly, this approach means the validators are automatically fired during the HTTP POST in the MVC app and the model state is automatically updated with a list of errors.
I use this approach extensively, albeit in MVC apps that call an internal API.
In the majority of my cases, the MVC client validates the model and if it passes the checks it then calls the API or service layer after with a DTO / service / entity model which is mapped with Automapper.
The MVC validation is typically light and checks for required fields, lengths, etc.
The API does validation again but it does it on the entity and it goes much deeper this time as it checks for duplicates, invalid entity state, etc. .
One last comment that I would like to add. I wouldn't throw exceptions on validation errors. The UI should use ModelState and the service layer returns a result which the client knows how to merge back into ModelState so either scenario results in the users getting a nice list of errors to deal with.
Hope this helps!
Generally you have 2 ways to perform validation:
Validate View Models (used in most cases)
Internal Business Entities validation (which most often used with 1-st)
For 1-st point you validate view models (on client and server), which placed in your Web project. In that case you should place view model validators in Web project too. [Validator(typeof(PersonValidator))] attribute is needed for linking view model parameter of action and action itself to perform validation before action execution. As in documentation:
Internally, FluentValidation’s MVC integration makes use of a validator factory to know how to work out which validator should be used to validate a particular type. By default, FluentValidation ships with an AttributedValidatorFactory that allows you to link a validator to the type that it validates by decorating the class to validate with an attribute that identifies its corresponding validator.
If you want to validate business models (2-nd point), not/not only view models, you need to place entity validators to Business project and register them in your IoC container (example with Castle Windsor), and change validator tool next way:
public class ValidatorTool
{
public static void FluentValidate<T>(IContainer container, T entity) // replace IContainer with your actual container interface name
{
var validator = container.Resolve<IValidator<T>>();
var result = validator.Validate(entity);
if (result.Errors.Any())
throw new ValidationException(result.Errors);
}
}
I have a requirement to encapsulate pieces of business logic within a transaction in an OData Web API service. Some of these pieces will need to accept one or more entities.
An example use case might be StockProduct and this might accept a Product entity and a Location entity. It would create the product and update stock records for the Location.
The approach I've taken is to create an unbound OData action that accepts these entities so that both of these can be operated on in a single transaction. Unfortunately neither can Entities be used as an ODataActionParameter nor can they be part of a class and used as a complex parameter.
I can think of a two ways around this:
Create a DTO class that is not an entity that is a mirror of each of my mirror classes and convert from DTO to Model within my action. The problem here is that I already have a DTO for each Model eg. Product.cs and ProductDTO.cs and don't really want to have to create a third class. (Currently, the ProductDTO.cs is used for Posts, Puts, Patches and Deletes and the Product.cs is used for Gets).
Abandon OData actions and create a simple end point that accepts whatever I like. I'm not keen on going down the second route as I'd like to use OData exclusively.
Any thoughts or suggestions?
You can use the ActionConfiguration.EntityParameter() method to bind an entity as a parameter to your OData action method.
Here is an example:
ActionConfiguration validate = ModelBuilder.EntityType<TEntity>()
.Collection.Action("Validate");
validate.Namespace = "Importation";
validate.EntityParameter<TEntity>(typeof(TEntity).Name);
validate.CollectionParameter<string>("UniqueFields");
validate.Returns<ValidationResult>();
However, note that the ModelState will not check against the content of the supplied Entity and will set any missing properties to null and properties exceeding the StringLength(x) annotation in your model will still pass. If you wish to validate the entity itself after, use this bit of code in your action method:
[HttpPost]
public virtual IHttpActionResult Validate(ODataActionParameters parameters)
{
//First we check if the parameters are correct for the entire action method
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
//Then we cast our entity parameter in our entity object and validate
//it through the controller's Validate<TEntity> method
TEntity Entity = (TEntity)parameters[typeof(TEntity).Name];
Validate(Entity, typeof(TEntity).Name);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IEnumerable<string> uniqueFields = parameters["UniqueFields"] as IEnumerable<string>;
bool result = Importer.Validate(Entity, uniqueFields);
return Ok(result);
}
}
As for your StockProductDTO, it seems to me that this is an new Business Entity by itself and should be treated as such.
You can use a batch request to perform multiple operations within a single request. This allows you to use your existing controllers for inserting your two objects.
https://aspnetwebstack.codeplex.com/wikipage?title=Web+API+Request+Batching
After a year or so of MVC experience I'm still confused about one thing: How to effectively use the DataAnnotations with ModelState.IsValid? For simple tutorial example this all works just fine and I have no questions about that. But supposed I have the following model:
Public Class Movie
Public Property MovieID As Integer
Public Property Title As String
Public Property Year As Integer
Public Property AddedByUser As String
End Class
Now the field AddedByUser is required in the database however I don't want the user to provide this but rather the business logic based on the currently logged in user. How would I use the DataAnnotation attributes for this scenario? If I make this field required then in the controller when I say:
Public Function SaveMovie(ByVal entity as Movie) As ActionResult
If ModelState.IsValid
// Save to DB here...
End If
Return View(entity)
End Function
... the validation will fail because I don't have that field in the view bindings. Should I have a hidden field for this? Should I write a custom view model for SaveMovie action? I suppose I could write my own validation in business logic but then why use model validation at all? Custom model binder perhaps? What is the best way to handle these types of scenarios?
Just to give another example scenario what about the difference between insert and update operation and validation? For update operations object's primary key is required. However that is not the case for inserts. Are you supposed to have separate models for insert and update just because of this one key property?
so the way that I handle this is I use the DataAnnotation based validation for user input type stuff. i.e. Validation on email addresses, dates, required fields etc. Stuff that you need a quick 'sanity check' on and need to double check the users entries on.
I don't put any DataAnnotations on the fields that my Database controls or my code controls, i.e. Primary Keys, your [AddedByUser] property as the user doesn't access these properties directly and so you shouldn't have to add validation checks on this. Since your code is the only thing that updates these properties, why validate them?
For more 'business rule' type validation I implement IValidatableObject on my model which gets run, in MVC, after all property-level validations have succeeded. Note that it won't run if the property-level validations fail. And this makes sense, because if the data is 'dirty' you wouldn't want to proceed to run more complex validation etc.
Hope this helps :)
Just curious on your thoughts or experiences around service layer validation.
I have to process fairly standard validation such as "object with name property doesn't already exist", but I wasn't sure how to return these validation failures back to the controller.
My initial thought was to implement a standard List<ValidationError> but I've seen it done each and every way so was curious the pros/cons of each.
Thanks for any input.
If you go with System.ComponentModel.DataAnnotations entries you can (as you seem to know) decorate your properties with required and many more tags
public class Person
{
[Required(ErrorMessage="object with name property doesn't already exist")]
public string Name { get; set; }
}
although I personally use ViewModels rather than exposing domain mdoels to the view, your controller action can now do something like:
[HttpPost]
public ActionResult SavePerson(Person model)
{
if (ModelState.IsValid)
{
// your model validates - do things
return RedirectToAction("success view here");
}
return View(model);
}
This is one of the standard 'post' handler patterns in MVC. This is the simplest path to getting your object model validating in my opinion.
From there, there are a few other options - your domain object can implement IValidatedableObject and you can yield return the errors (see http://buildstarted.com/2010/09/20/mvc-3s-ivalidatableobject/ as an example).
I'd recommend not mixing the two though, as if you are using dataannotations and have even a single invalid property, the IsValid method on IValidatableObject will not be called.
From there, there's lots you can do with custom validation attributes (the extended version of IsValid seems to give you more flexibility http://msdn.microsoft.com/en-us/library/gg480674%28v=vs.98%29.aspx)
Hope some of the above helps - once you get past the basics there's a lot you can do with it and things like client validation of custom attributes etc. are all fun.
Cheers,
Terry
[edit to add:
After re-reading your post, it may be that you want to only validate at the service layer? If so, I've used the following approach:
public void Setname(string newName)
{
Validator.ValidateProperty(newName, new ValidationContext(this, null, null) { MemberName = "Name" });
Name = newName;
}
obviously your Name property would need a { get; private set; } for this, though you could always add the Validator.ValidateProperty into an extended setter for the public property either.
]
On a new project I'm working on (first time mvc) I've been using the ms code contracts (which throw exceptions) and do all the validation on my domain objects themselves. For things that can't be validated there (such as validations that require database access) I validate in my services and throw exceptions. Additionally like the poster above I have whole separate view models for everything that have data annotations validators on them. The exceptions bubble up and I catch them in the controller and append to the ModelState. There's a lot of overlap with those and the view model validation but it's not much extra effort and allows me to vary the validation per view and yet still have the "core" validations be required.
The book pro asp mvc 2 has another nice way - write a class that inherits Exception and contains a collection of errors. Then you do your validations, add to the collection then throw the exception then he catches it in the controller and copies over to ModelState. This method will let you catch ALL the errors in one exception instead of just one at the service layer.
I'm begginer in asp.net mvc and i have some doubts.
P.S: I'm using DDD to learn
I have an ACtion in a Controller and it'll save an entity (from my model) by a repository (for a database).
My doubts is, How can I get the informations from the View and save it by a repository in my Controller ?
Is it correct to get an entity of my Model in Save method of controller, like this:
public ActionResult Save(Product product)
{
// validate object
// save data in repository
return View("Success");
}
Or Need I get an DTO (with a structure similar to my entity) and create an object passing property by property to an entity ?
I didnt' like of FormCollection and I'd like to know, What is recommended architecturally ?
Thanks a lot guys
Cheers
I you want to follow DDD practices as described by the Blue Book you should bind your views to DTO's which can be forwarded to a thin 'Application' layer where Domain objects are created or retrieved from database. This application layer can be a simple facade with methods or can leverage command pattern.
For a live demo, you can see my project -- DDDSample.NET
This kind of problem can be fixed by adding so called view model.
Basically - a view model is DTO that provides data for particular view. In similar fashion - view models are used to get data back from view via model binding. Then - controller just forwards necessary data to domain model.
Typically, in ASP.NET MVC your controller actions will receive strongly typed objects returned by the DefaultModelBinder when editing entity types. Using this pattern you can pass a "Product" to your GET view either by itself or as part of a DTO and then your "Save" method would receive a "Product" object in its parameter list.
So long as you are using either editor templates or fields with matching names (i.e. Html.TextBox("Name") corresponds to Product.Name) then the DefaultModelBinder should be able to correctly fill the typed entity object passed to the action method. You shouldn't have to mess with the FormCollection except in certain edge cases.
[HttpGet]
public ActionResult Create() {
return View("Create", new Product());
}
[HttpPost]
public ActionResult Create(Product product) { //or Save(Product)
...
}
As long as your form has fields which match the fields in Product, they should automatically be populated for you based on the values. How you save the entity depends on the data model, whether you're creating a new record or editing an existing one, etc.