简体   繁体   中英

Where should I validate adding to my entity's collection property?

I'm just about to start a new pet project and I've been wondering about how I should go about validation when adding an entity to a parent's one-to-many collection. I'll use two example classes to summarize what I'm going on about a Student and a Teacher . Constraint here is that at any given time a Student can only be taught by one (and only one) Teacher who in-turn could be teaching one or more Student s).

public class Student
{
    public bool IsEnrolled { get; set; }

    public virtual Teacher IsCurrentlyBeingTaughtBy { get; set; }
}

public class Teacher
{
    public virtual ICollection<Student> IsCurrentlyTeaching { get; set; }
}

When students arrive at a class I need to assign them to the Teacher 's IsCurrentlyTeaching collection, but I first need to make sure they're enrolled. My question is where best to validate this basic rule? The options going around my head currently are:

1. Use a repository pattern

As I'm going to be applying unit tests I'm leaning in favor of this method as I can wrap my data access logic up into a mockable object and there is a single responsibility here so I only have to validate this in my repository once. BUT - is validation the responsibility of the repository, or should I be only dealing with the CRUD of entities in a repository?

2. Validate this in the controller action

I should mention here that I propose this to be an MVC3 project, so keeping specifically to that should I be performing this validation in the controller's action before adding the Student to the repository (and subsequently the Teacher 's list of students they're currently teaching). BUT - am I heading down a fat controller path that I really shouldn't be?

3. Perform this validation on the Teacher entity

Cutting out the middle-man (ie repository) should I be adding the Student via a method on the Teacher POCO such as AddStudent(Student student) and throwing a custom exception when trying to add a student who hasn't been enrolled?

There are probably more options available, but these are the three I'm trying to choose between at this present moment and I've got a little tunnel vision from thinking about this. Obviously all of the above can be suitably unit tested but thinking long-term (and accommodating growth) which path should I be heading down?

You may be able to create your own custom validator for this. That would let you piggyback on the validation the MVC is already providing. I've never tried this, but I would imagine something like this would work:

public class EnsureEnrollment : ValidationAttribute
{
    public EnsureEnrollment () {    }

    public override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var studentList = value as IEnumerable<Student>;
        if (studentList == null)
        {
            return ValidationResult.Success;
        }

        foreach(Student s in studentList )
        {
            if(!s.IsEnrolled)
            {
                //Insert whatever error message you want here.  
                return new ValidationResult("Student \"" + s.Name + "\" is not enrolled.");
            }
        }

        return ValidationResult.Success;
    }
}

Then on your property just add your annotation:

[EnsureEnrollment()]
public virtual ICollection<Student> IsCurrentlyTeaching { get; set; }

Personally, I like having my validation as part of static CRUDL methods on my entities. True, you have to pass the context in to every one of them, but it keeps the controllers a lot cleaner and makes all of that functionality readily available for any other projects that may use your entities in the future.

Previously I created a base class that all of my entities derived from which had a must override for Validate. The Validate method was called by almost all of the CRUDL methods and other working methods to ensure that the entity was proper before acting on it. Most of these validation rules were a bit more complex that could be easily expressed using the DataAnnotations attributes.

Or you can integrate specific validation points into a method with a more specific purpose. Take for instance:

    public static bool AddToTeacher(SchoolContext db, Student student, Teacher teacher)
    {
        if (student.IsEnrolled)
        {
            teacher.IsCurrentlyTeaching(student);
            return db.SaveChanges() > 0;
        }
        return false;
    }

The AddToTeacher method only ensures that a specific requirement is met. If I wanted to ensure that the student was properly formed and was of eligible course track and what not, I would likely write a short method (or several all called by a "container" method) to validate those particular points.

In short, I do my best to keep every bit of entity specific code on the entity so that the controller is mostly ignorant of how the entities work.

As for on which entity to put it, it depends on how you think. Student.AddToTeacher is just as viable in my opinion as Teacher.AddStudent. I personally would use the former just because that is what most of my entities currently look like with "child" entities adding themselves to "parents" rather than the other way around.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM