我正在寻找一种解决方案,当用户使用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之前,您应该如何对实体执行复杂的验证(交叉字段,查询等)?
答案 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