简体   繁体   English

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

[英]How to validate entity against the context in Entity Framework?

I usually validate entities in Entity Framework by calling entity.IsValid() and creating appropriate ValidationAttribute class for the entity.我通常通过调用 entity.IsValid() 并为实体创建适当的 ValidationAttribute 类来验证实体框架中的实体。

Now, however, I run into a case when I need to validate an entity not just on its own, but within the context to which it belongs, as an example:但是,现在我遇到了这样一种情况:我不仅需要验证实体本身,还需要在它所属的上下文中验证实体,例如:

[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;
     }
}

There is a difficulty: simply using context.Courses.Add(course) does not cause context.Courses.Where(c => c.Name == course.Name) to return anything.有一个困难:简单地使用context.Courses.Add(course)不会导致context.Courses.Where(c => c.Name == course.Name)返回任何内容。 Instead, it will require context.SaveChanges() before the entity is available as part of the entire collection.相反,它需要context.SaveChanges()在实体作为整个集合的一部分可用之前。 Which means that I will not be able to validate the entity against the collection before trying to save it to database.这意味着在尝试将其保存到数据库之前,我将无法根据集合验证实体。

I know this example is simplistic and can be handled by a database-side unique constraint, but even if we're not going to look at more complex examples, I see a good reason to filter the invalid entries before trying to commit them to the database (because if one entry in the transaction offends a constraint, the entire transaction will be blocked), and to put all validation criteria in a single place, instead of splitting them between different classes and/or database schema (to maintain single responsibility).我知道这个例子很简单,可以由数据库端唯一约束处理,但即使我们不打算看更复杂的例子,我也看到了在尝试将无效条目提交到数据库(因为如果事务中的一个条目违反了约束,则整个事务将被阻止),并将所有验证标准放在一个地方,而不是将它们拆分到不同的类和/或数据库模式(以保持单一职责) .

In which ways can a validation strategy be implemented to satisfy these requirements?可以通过哪些方式实施验证策略来满足这些要求?

What we do is override the context's ValidateEntity method .我们所做的是覆盖上下文的ValidateEntity 方法 This gives you a chance to check things in the database (like duplicates, etc) before committing the changes.这使您有机会在提交更改之前检查数据库中的内容(如重复项等)。

Simply add the overriden class to your context and do any checks you need:只需将覆盖类添加到您的上下文中并执行您需要的任何检查:

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);
}

Now you can call context.GetValidationErrors() and deal with the error(s) before saving.现在您可以调用context.GetValidationErrors()并在保存之前处理错误。 For example here .例如这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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