I am trying to implement a validation strategy in my application. I have an MVC layer, Service layer, Repository and Domain POCOs. Now at the MVC layer, i use data annotations on my viewmodels to validate user input, allowing me to give the user quick feedback. In the controller, I call ModelState.IsValid to check the input before using automapper to set up a domain object.
Here's where my trouble is. I pass my domain object into the Service which needs to validate it against my business rules but how do I pass validation errors back to the controller? Examples I have found do one of the following:
Is there a simpler method I am missing? I have seen lots of questions regarding this but no concrete solution other than those mentioned above. I am thinking that any method in the Service which does validation could just pass back some key/value object that I can use in the controller but am unsure if this strategy could be problematic later on.
Implement validations in the domain model layer. Validations are usually implemented in domain entity constructors or in methods that can update the entity.
As a general rule of thumb, I would say that business logic of this sort should be in the service. Controllers should be light-weight and pass on requests. Further, there may be other clients of your service, not just controllers, so this allows you to keep validation in one place.
Service layer is an architectural pattern, applied within the service-orientation design paradigm, which aims to organize the services, within a service inventory, into a set of logical layers. Services that are categorized into a particular layer share functionality.
The service layer consists of a collection of Java classes that implement business logic (data retrieval, updates, deletions, and so on) through one or more high-level methods. In other words, the service layer controls the workflow.
I think quite an elegant way would be to have a validate method on your service that returns a dictionary of model errors from business logic. That way, there is no injection of the ModelState to the service - the service just does validation and returns any errors. It is then up to the controller to merge these ModelState errors back into it's ViewData.
So the service validation method might look like:
public IDictionary<string, string> ValidatePerson(Person person)
{
Dictionary<string, string> errors = new Dictionary<string, string>();
// Do some validation, e.g. check if the person already exists etc etc
// Add model erros e.g.:
errors.Add("Email", "This person already exists");
}
And then you could use an extension method in your controller to map these errors onto the ModelState, something like:
public static class ModelStateDictionaryExtensions
{
public static void Merge(this ModelStateDictionary modelState, IDictionary<string, string> dictionary, string prefix)
{
foreach (var item in dictionary)
{
modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key, item.Value);
}
}
}
And your controller would then use:
ModelState.Merge(personService.ValidatePerson(person), "");
As an alternative to creating an intermediate dictionary as suggested by Ian, you could also do this with a validator that accepts a function.
e.g. in the service layer:
public void ValidateModel(Customer customer, Action<string, string> AddModelError)
{
if (customer.Email == null) AddModelError("Email", "Hey you forgot your email address.");
}
Then in your controller you validate with a single call:
myService.ValidateModel(model, ModelState.AddModelError);
Or say you want to use your validator in a console app without access to a ModelStateDictionary, you could do this:
errors = new NameValueDictionary();
myService.ValidateModel(model, errors.Add);
Both of these work because
ModelStateDictionary.AddModelError()
and
NameValueDictionary.Add()
match the method signature for Action<string, string>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With