我正在寻找一种解决方案,当用户使用实体框架
将其更改保存在动态数据屏幕中时,执行一些自定义实体验证(这需要数据库访问、跨成员验证......验证比我对属性可以做的事情更复杂(它需要访问数据库等)
你能拦截保存更改调用吗?
我试图覆盖 DbContext 对象中的ValidateEntity
,但动态数据似乎没有调用它(可能是因为它使用的是内部 ObjectContext,不知道为什么),覆盖 SaveChanges 也无济于事。
我没有看到任何我可以订阅的活动...
文档应该有帮助:
通过覆盖 OnValidate 方法或处理 Validate 事件,这些事件被调用 当任何数据字段发生更改时。此方法允许您添加验证 以及单个字段的业务逻辑。这种方法更多 比为单个字段添加验证更通用。它很有用 当相同的验证逻辑可以应用于多个数据时 田。它还允许您执行涉及的验证检查 多个字段。
但是我使用的是 POCO 实体框架 6 类,因此没有OnValidate
方法可以重写,从我读到的内容来看,这是针对 LinqToSql 的,我找不到他们提到的Validate
事件。
我尝试在 DbContext 的构造函数中订阅内部ObjectContext
的 SavingChanges
事件,以手动调用 ValidateEntity
,但我不知道如何处理结果。如果我抛出一个DbEntityValidationException
(或本文中建议的ValidationException
),ASPNET 会像处理任何异常(黄屏)一样处理它。
实现IValidatableObject
也不起作用。
我也尝试实现我自己的DynamicValidator
看看会发生什么,但没有成功,它似乎处理了异常(如果我覆盖ValidateException
,并放置一个断点,我会看到它),但它仍然冒泡到默认错误处理程序并显示黄色屏幕。我一定错过了什么。
那么,您应该如何执行复杂的验证(跨字段、查询等) 在保存在动态数据/EF中之前在实体上?
像你试图执行的逻辑不属于你的架构中的这样一个级别。让数据库强制执行它应该执行的约束,如外键等,并使您的业务逻辑在上面一层。例如,在要验证的实体上,可以添加一个IsValidForAddOrUpdate()
方法,该方法包含您无论如何都会放入验证程序的逻辑。然后只需利用新方法:
if (entity.IsValidForAddOrUpdate())
{
db.Set<Entity>().Add(entity);
db.SaveChanges()
}
else throw new DbValidationException("Entity failed validation due to rule xyz.");
实现这一点的一种方法是在您的实体上实现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,您可以使用此答案。然后重写上下文的 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();
}
另请参阅此答案以使其与数据注释一起使用。
你写道:
如果我抛出一个 DbEntityValidationException(或 ValidationException) 就像本文建议的那样),ASPNET 像处理任何异常一样处理它 (黄屏)。
看到这个答案。当您调用SaveChanges
时,您需要捕获DbEntityValidationException
(或ValidationException
),如果您没有捕获它们以在控制器中处理它们,则它们由默认错误处理程序处理。
或者,您可以使用 DynamicValidator 控件来捕获 ValidationExceptions:
<!-- Capture validation exceptions -->
<asp:DynamicValidator ID="ValidatorID" ControlToValidate="GridView1"
runat="server" />
DynamicValidator
的问题在于它需要ControlToValidate
属性,并且仅捕获来自该控件的异常。此外,封装在其他异常中的异常可能会造成问题。有一种解决方法 - 您可以从DynamicValidator
继承并覆盖其ValidateException
方法,请参阅此博客。
请参阅此文章。
我发现了一个我不喜欢的解决方法,但它有效:
我的上下文仍在执行验证,并在必要时抛出ValidationException
。
由于ListView
似乎没有捕获和处理异常,因此我通过处理ListView
的OnItemUpdated
或OnItemInserted
事件来自己执行此操作:
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 });
}
}