Пользовательская расширенная проверка объекта с помощью динамических данных
Я ищу решение для выполнения некоторой проверки пользовательских сущностей (для чего требуется доступ к базе данных, проверка соответствия между членами...), когда пользователь сохраняет свои изменения на экране динамических данных с помощью Entity Framework.
Проверка более сложна, чем то, что я могу сделать с атрибутами (для этого требуется доступ к базе данных и т.д.)
Вы можете перехватить вызов SaveChanges?
Я попытался переопределить ValidateEntity
в объекте DbContext, но Dynamic Data, похоже, не вызывает его (возможно, потому, что он использует внутренний ObjectContext, не уверен, почему) и переопределение SaveChanges тоже не помогает.
Я не вижу никакого события, которое я мог бы подписаться на...
Документация должна помочь:
Настроить проверку для отдельного поля данных путем переопределения OnValidate или обрабатывать событие Validate, которое вызывается при изменении любого поля данных. Этот подход позволяет добавить подтверждение и бизнес-логику для отдельной области. Этот подход более чем добавление проверки для отдельного поля. Это полезно когда одна и та же логика проверки может применяться к нескольким данным поле. Он также позволяет выполнять проверки, которые включают несколько полей.
Но я использую классы POCO Entity Framework 6, поэтому нет метода OnValidate
для переопределения, и из того, что я прочитал, это для LinqToSql, и я не могу найти событие Validate
, которое они упоминают.
Я попытался подписаться на событие SavingChanges
внутреннего ObjectContext
в конструкторе моего DbContext, чтобы вручную вызвать ValidateEntity
, но я не знаю, что делать с результатом. Если я выброшу DbEntityValidationException
(или ValidationException
, как предлагается в этой статье), ASPNET обрабатывает его как любое исключение (желтый экран).
Реализация IValidatableObject
тоже не работает.
Я также попытался реализовать свой собственный DynamicValidator
, чтобы увидеть, что происходит, но без успеха, похоже, обрабатывает исключение (если я переопределяю ValidateException
и ставил точку останова, я это вижу), но он все еще пузырился к обработчику ошибок по умолчанию и отображает желтый экран. Мне что-то не хватает.
Итак, как вы должны выполнять сложную проверку (кросс-поле, с запросами и т.д.) для объектов перед сохранением в Dynamic Data/EF?
Ответы
Ответ 1
Я нашел обходное решение, которое мне не нравится, но оно работает:
Мой контекст все еще выполняет валидацию и бросает a ValidationException
, если это необходимо.
Поскольку ListView
, похоже, не захватывает и не обрабатывает исключение, я делаю это сам, обрабатывая событие OnItemUpdated
или 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
используется для добавления сообщения об исключении в сводку проверки. Он добавляет "фальшивый", всегда неудачный валидатор с сообщением.
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
Я бы сказал, что логика, как вы пытаетесь выполнить, не входит в такой уровень в вашей архитектуре. Пусть база данных обеспечивает ограничения, которые она должна использовать, например, внешние ключи и т.д., И иметь свою бизнес-логику выше. Например, на вашей сущности, которую вы хотите проверить, вы можете добавить метод IsValidForAddOrUpdate()
, который в любом случае содержит логику, которую вы положили в ваши валидаторы. Затем просто используйте новые методы:
if (entity.IsValidForAddOrUpdate())
{
db.Set<Entity>().Add(entity);
db.SaveChanges()
}
else throw new DbValidationException("Entity failed validation due to rule xyz.");
Ответ 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
}
Чтобы получить доступ к текущему методу DBContext из методов IDataErrorInfo, вы можете использовать этот ответ.
Затем переопределите метод 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();
}
См. также этот ответ, чтобы он работал с DataAnnotations.
Вы писали:
Если я выкидываю исключение DbEntityValidationException (или исключение ValidationException как это предлагается в этой статье), ASPNET обрабатывает его как любое исключение (желтый экран).
См. этот ответ. Когда вы вызываете SaveChanges
, вам нужно поймать DbEntityValidationException
(или ValidationException
), если вы не поймаете их для обработки их в своем контроллере, они обрабатываются обработчиком ошибок по умолчанию.
Или вы можете использовать DynamicValidator для проверки ValidationExceptions:
<!-- Capture validation exceptions -->
<asp:DynamicValidator ID="ValidatorID" ControlToValidate="GridView1"
runat="server" />
Проблема с DynamicValidator
заключается в том, что для этого требуется свойство ControlToValidate
, и он захватывает только исключения, исходящие из этого элемента управления. Кроме того, исключения, заключенные в другие исключения, могут создавать проблемы. Существует обходное решение - вы можете наследовать от DynamicValidator
и переопределить его метод ValidateException
посмотреть этот блог.
См. в этой статье.