如何根据实体框架中的上下文验证实体?

时间:2018-03-02 18:57:54

标签: c# .net entity-framework validation

我通常通过调用entity.IsValid()并为实体创建适当的ValidationAttribute类来验证Entity Framework中的实体。

然而,现在我遇到一个案例,当我需要验证一个实体时,不仅仅是它自己,而是在它所属的上下文中,作为一个例子:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CourseValidation : ValidationAttribute
{
    public CourseValidation() {}
    protected override ValidationResult IsValid (object value, ValidationContext validationContext)
    {
        List<string> messages = new List<string>();
        if (value is Course)
        {
            Course course = (Course)value;
            if (course.Context != null)
            {
                if (course.Context.Courses.Any(c => c.Name == course.Name && c.Department.ID == course.Department.ID))
                {
                    messages.Add($"Cannot create a course with name {course.Name} in {course.Department.Name} department because a course with this name already exists in this department.");
                }
            }
            else messages.Add("Course is being improperly handled by the software, please contact support department");
         }
         else messages.Add("Course is expected, but does not exist");

         if (messages.Count > 0) return new ValidationResult(string.Join(Environment.NewLine, messages));
         else return ValidationResult.Success;
     }
}

存在一个难点:仅使用context.Courses.Add(course)不会导致context.Courses.Where(c => c.Name == course.Name)返回任何内容。相反,在实体作为整个集合的一部分可用之前,它将需要context.SaveChanges()。这意味着在尝试将实体保存到数据库之前,我将无法对集合验证实体。

我知道这个例子很简单,并且可以通过数据库端唯一约束来处理,但即使我们不打算查看更复杂的示例,我也看到了在尝试之前过滤无效条目的充分理由将它们提交到数据库(因为如果事务中的一个条目冒犯了约束,整个事务将被阻止),并将所有验证标准放在一个地方,而不是在不同的类和/或数据库模式之间拆分它们(保持单一责任。)

在哪些方面可以实施验证策略以满足这些要求?

1 个答案:

答案 0 :(得分:1)

我们所做的是覆盖上下文ValidateEntity method。这使您有机会在提交更改之前检查数据库中的内容(如重复项等)。

只需将覆盖类添加到您的上下文中,然后执行所需的任何检查:

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
    IDictionary<object, object> items)
{

    if (entityEntry.Entity is Course &&
                (entityEntry.State == EntityState.Added 
                  || entityEntry.State == EntityState.Modified))
    {
        var courseToCheck = ((Course)entityEntry.Entity);

        //check for uniqueness 
        if (Courses.Any(c => c.Name == course.Name && c.Department.ID == course.Department.ID)))
            return 
                   new DbEntityValidationResult(entityEntry,
                 new List<DbValidationError>
                     {
                         new DbValidationError( "Name",
                             $"Cannot create a course with name {courseToCheck .Name} in {courseToCheck .Department.Name} department because a course with this name already exists in this department.")
                     });
    }

    return base.ValidateEntity(entityEntry, items);
}

现在您可以在保存之前调用context.GetValidationErrors()并处理错误。例如here