【问题标题】:Custom advanced entity validation with Dynamic Data使用动态数据自定义高级实体验证
【发布时间】:2016-08-22 21:56:15
【问题描述】:

我正在寻找一种解决方案,以在用户使用 Entity Framework 将其更改保存在动态数据屏幕中时执行一些自定义实体验证(这将需要数据库访问、跨成员验证...)。
验证比我可以对属性执行的操作更复杂(它需要访问数据库等)

你能拦截 SaveChanges 调用吗?
我试图覆盖 DbContext 对象中的ValidateEntity,但动态数据似乎没有调用它(可能是因为它使用的是内部 ObjectContext,不知道为什么),并且覆盖 SaveChanges 也无济于事。
我没有看到任何可以订阅的活动...

documentation 应该会有所帮助:

通过覆盖 OnValidate 方法或处理被调用的 Validate 事件 当任何数据字段更改时。这种方法允许您添加验证 和单个字段的业务逻辑。这种做法更 一般而不是为单个字段添加验证。它是有益的 当相同的验证逻辑可以应用于多个数据时 场地。它还允许您执行验证检查,包括 多个字段。

但是我使用的是 POCO Entity Framework 6 类,所以没有 OnValidate 方法可以覆盖,从我读到的内容来看,这是针对 LinqToSql,我找不到他们提到的 Validate 事件。

我尝试在我的 DbContext 的构造函数中订阅内部ObjectContextSavingChanges 事件,以手动调用ValidateEntity,但我不知道如何处理结果。如果我抛出一个DbEntityValidationException(或ValidationException,就像this article 中建议的那样),ASPNET 会像处理任何异常(黄屏)一样处理它。

实现IValidatableObject 也不起作用。

我也尝试实现我自己的DynamicValidator 来看看会发生什么,但没有成功,它似乎处理了异常(如果我覆盖ValidateException,并放置一个断点,我看到了)但它仍然冒泡到默认错误处理程序并显示黄色屏幕。我一定是错过了什么。

那么,在保存到动态数据/EF 之前,您应该如何对实体执行复杂的验证(跨字段、查询等)?

【问题讨论】:

    标签: c# entity-framework validation asp.net-dynamic-data


    【解决方案1】:

    我找到了一个我不喜欢的解决方法,但它确实有效:

    My Context 仍在执行验证并在必要时抛出ValidationException

    由于ListView 似乎没有捕获和处理异常,我自己通过处理ListViewOnItemUpdatedOnItemInserted 事件来完成:

    protected void ListView1_ItemUpdated(object sender, ListViewUpdatedEventArgs e)
    {
        if (e.Exception != null)
        {
            ValidationError.DisplayError(e.Exception.Message);
            e.ExceptionHandled = true;
            e.KeepInEditMode = true;
        }
    }
    

    ValidationError 用于将异常消息添加到验证摘要中。它添加了一个“假的”,总是带有消息的验证器失败。

    public class ValidationError : BaseValidator
    {
        private ValidationError(string message)
            : base()
        {
            ErrorMessage = message;
            IsValid = false;
        }
    
        protected override bool EvaluateIsValid()
        {
            return false;
        }
    
        public static void DisplayError(string message, string validationGroup)
        {
            var currentPage = HttpContext.Current.Handler as Page;
            currentPage.Validators.Add(new ValidationError(message) { ValidationGroup = validationGroup });
        }
    }
    

    【讨论】:

      【解决方案2】:

      实现这一目标的一种方法是在您的实体上实现IDataErrorInfo 接口,如下所示:

      public partial class MyEntity : IDataErrorInfo
      {
          public MyEntity()
          {
          }
      
          ...
      
          #region IDataErrorInfo Members
          public string Error
          {
              get { throw new NotImplementedException(); }
          }
          public string this[string propertyName]
          {
              get
              {
                  //Custom Validation logic
                  return  MyValidator.ValidateProperty(this, propertyName);
              }
          }
          #endregion  
      }
      

      要从 IDataErrorInfo 方法访问当前 DBContext,您可以使用 this answer。 然后覆盖你的 Context 的 SaveChanges 方法:

          public override int SaveChanges()
          {
              this.ObjectContext.DetectChanges();
      
              // Get all the new and updated objects
              var objectsToValidate =
              ChangeTracker.Entries().Where(x => x.State == EntityState.Modified || x.State == EntityState.Added).
              Select(e => e.Entity);
      
              // Check each object for errors
              foreach (var obj in objectsToValidate)
              {
                  if (obj is IDataErrorInfo)
                  {
                      // Check each property
                      foreach (var property in obj.GetType().GetProperties())
                      {
                          var columnError = (obj as IDataErrorInfo)[property.Name];
                          if (columnError != null) {
                          //Handle your validation errors
                          throw new DbEntityValidationException(columnError); }
                      }
                  }
              }
      
              return base.SaveChanges();
          }
      

      另请参阅this answer 以使其与 DataAnnotations 一起使用。

      你写道:

      如果我抛出 DbEntityValidationException(或 ValidationException 就像本文中建议的那样),ASPNET 像处理任何异常一样处理它 (黄色屏幕)。

      this answer。当您调用SaveChanges 时,您需要捕获DbEntityValidationException(或ValidationException),如果您没有捕获它们以在您的Controller 中处理它们,它们将由默认的错误处理程序处理。

      或者你可以使用DynamicValidator控件来捕获ValidationExceptions:

          <!-- Capture validation exceptions -->
          <asp:DynamicValidator ID="ValidatorID" ControlToValidate="GridView1" 
              runat="server" /> 
      

      DynamicValidator 的问题在于它需要ControlToValidate 属性并且它只捕获来自该控件的异常。此外,封装在其他异常中的异常也会产生问题。有一个解决方法 - 您可以从 DynamicValidator 继承并覆盖其 ValidateException 方法 see this blog

      this article

      【讨论】:

      • 是的,正是我发现的,但 DynamicValidator 没有捕捉到异常,我不知道为什么。如果我调试,我看到它进入了 ValidateException 方法,它似乎检测到它并用它做事(source code),但异常仍然上升到默认的错误处理程序。我什至尝试了 Dynamic Data Futures 包中的 ImprovementDynamicValidator,但它也不起作用。我无法在控制器中捕获它,因为动态数据没有。
      • 我明白了,有趣的问题,如果没有同时得到答复,我会在星期四看看。
      【解决方案3】:

      我认为像您尝试执行的逻辑不属于您的架构中的这样一个级别。让数据库强制执行它应该执行的约束,例如外键等,并让您的业务逻辑在上面一层。例如,在您想要验证的实体上,您可以添加一个IsValidForAddOrUpdate() 方法,该方法包含您无论如何都要放入验证器中的逻辑。然后使用新方法:

      if (entity.IsValidForAddOrUpdate())
      {
          db.Set<Entity>().Add(entity);
          db.SaveChanges()
      }
      else throw new DbValidationException("Entity failed validation due to rule xyz.");
      

      【讨论】:

      • 我同意这个说法,我也认为业务逻辑不应该和实体框架耦合
      • 这是有争议的。也许域层对象可以进行验证。只要不使用糟糕的“EF前面的存储库层”模式......
      • 我同意,但动态数据并没有提供很多验证选项。但问题不存在,问题是 DbValidationException 没有被 DynamicValidator 捕获(尽管我发现的文档和文章说它应该)所以我得到一个黄屏。现在我什至没有尝试拥有一个好的架构,只是为了拥有一些可行的东西:(。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多