使用动态数据自定义高级实体验证

时间:2016-04-28 08:42:30

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

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

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

documentation应该有所帮助:

  

通过覆盖个人数据字段来自定义验证   OnValidate方法或处理被调用的Validate事件   何时更改任何数据字段。此方法允许您添加验证   和单个领域的业务逻辑。这种方法更多   通常比为单个字段添加验证。它是有益的   当相同的验证逻辑可以应用于多个数据时   领域。它还允许您执行涉及的验证检查   多个领域。

但是我正在使用POCO Entity Framework 6类,因此没有OnValidate方法可以覆盖,而且从我读到的内容是LinqToSql,我找不到他们提到的Validate事件。

我尝试在我的DbContext的构造函数中订阅内部SavingChanges的{​​{1}}事件,手动调用ObjectContext,但我不知道如何处理结果。如果我在this article中抛出ValidateEntity(或DbEntityValidationException之类的建议,则ASPNET会像任何异常一样处理它(黄色屏幕)。

实施ValidationException也不起作用。

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

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

3 个答案:

答案 0 :(得分:4)

我认为像你试图执行的逻辑并不属于你的架构中的这样一个级别。让数据库强制执行它应该具有的约束,比如外键等,并将您的业务逻辑放在上面。例如,在您想要验证的实体上,您可以添加wc -c template_entry.xml方法,其中包含您在验证器中放置的逻辑。然后只使用新方法:

IsValidForAddOrUpdate()

答案 1 :(得分:3)

如何实现这一目标的一种方法是在您的实体上实现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

答案 2 :(得分:0)

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

我的上下文仍在执行验证,并在必要时抛出ValidationException

由于ListView似乎没有抓住并处理异常,我自己通过处理OnItemUpdated的{​​{1}}或OnItemInserted事件来执行此操作:

ListView

protected void ListView1_ItemUpdated(object sender, ListViewUpdatedEventArgs e) { if (e.Exception != null) { ValidationError.DisplayError(e.Exception.Message); e.ExceptionHandled = true; e.KeepInEditMode = true; } } 用于将异常消息添加到验证摘要中。它添加了一个“假的”,总是失败的验证器和消息。

ValidationError