Reputation: 2698
Supposing I had, by way of demonstration, a controller that looked like this:
public class ProjectController : Controller
{
private IProjectRepository projectRepository;
public ProjectController()
{
DBContext context = new DBContext();
this.projectRepository = new ProjectRepository(context);
}
public ActionResult Create(Project project)
{
if (ModelState.IsValid)
{
// do whatever
}
else
{
return View(project);
}
}
}
And suppose that this was the controller for a model that looked like this:
public class Project : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// this is where I would like to add code
}
}
My question is: is there really no way in the validation code to reference the projectRepository object on the controller? Yes, I could technically add quasi-validation to the Create() function on the controller, do the check there, and add errors directly - but in the real case, there are a number of actions that would all perform the same validation, which is really tied to the model you're trying to create (or edit, or copy, or whatever else). But it's tied to the model and to other existing instances of the same model, which only the controller knows how to query for. Is there any way to get around that?
(The goal is to check that the current model object under validation isn't the same as one that exists already; I'm open to other suggestions of how to do that as well, it just seemed like it clearly should be a job for standard validation code, either using IValidatableObject or using a ValidationAttribute. But I am not an expert in .net MVC validation, or for that matter, .net MVC at all.)
Thanks!
Upvotes: 0
Views: 275
Reputation: 1542
You might be better of putting your validation in the service layer
The online scaffolding tool CamoteQ generate its model validation code this way, which is a good reference for self-study.
Upvotes: 1
Reputation: 3082
IMO, there is a bit of a convention issue at play. The model that the controller returns to the client is a ViewModel, not an entity. This comes into play when thinking about which objects have knowledge of dependent objects.
The repository deals with models (entities), and the controller deals with ViewModels. ViewModels are really just a bunch of data and formatting, so set-level validations don't make sense on a ViewModel.
And really want the business-layer or repository to perform set-level validation, not the model itself. You could set a reference to the repository on the model when it is created and have the model call the repository for set-level validation. But this becomes problematic when you want to clone or deserialize the entity.
By the way, EntityFramework solves these problems by allowing you to Attach
a disconnected entity. You might want to use EF instead of the repository pattern.
But for you immediate issue, I would not try to perform set-level validation from within the entity or viewmodel.
Upvotes: 1
Reputation: 16743
IValidatableObject
belongs to the DataAnnotations
namespace. To my mind, Data Annotations are great for input validation. Where they start to fall down is when you start applying complex business rules where your domain model's validity depends on the state of other domain models.
When that happens, introduce a service layer. Put all your business rules in there, and allow the service to mediate the conversation between your models. At the end of the day, a service is supposed to be the interface with which you communicate with your model.
This is where I usually say to myself, "Hey, your app has now reached the 'medium-complexity' stage"! :)
An older but still relevant tutorial can be found here: http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-a-service-layer-cs
Upvotes: 3