Entity Framework IValidatableObject ссылка DbContext
Я пытаюсь заставить EF 4.1 работать с репозиторием, UnitOfWork, отделением сущностей от EF и проверкой.
Я следовал этому руководству, чтобы получить хорошее разделение моих сущностей POCO от модели EF, и сейчас я следую этому руководству для реализации валидации (с IValidatableObject).
Мое решение состоит из:
- Contacts.Repository [ссылки EF и Contacts.Entities]:
- Contacts.edmx
- ContactsDbContext.cs
- Contacts.Entities [нет ссылок]:
- Contact.cs (Contacts.Entities.Contact частичный класс)
- Contacts.Validation [ссылки Contacts.Entities и Contacts.Repository]
- Contact.cs (Contacts.Entities.Contact частичный класс)
Но я бью кирпичную стену с проверкой:
- Я не могу добавить логику проверки в Contacts.Entities, потому что это вызовет циклическую ссылку с Contacts.Repository (contact.Validate(...) должен использовать ContactsDbContext). Поэтому я создал отдельный проект Contacts.Validation.
- Но это означает разделение класса Contact с частичными классами для определения Contact внутри Contacts.Entities и Contacts.Validation. Код больше не компилируется, потому что вы не можете определить частичный класс для разных сборок.
У кого-нибудь есть указания для меня здесь? Я разместил код ниже...
Contacts.Repository.ContactsDbContext.cs:
namespace Contacts.Repository
{
public partial class ContactsDbContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
items.Add("Context", this);
return base.ValidateEntity(entityEntry, items);
}
}
}
Contacts.Entities.Contact.cs:
namespace Contacts.Entities
{
public partial class Contact
{
public string Name { get; set; }
}
}
Contacts.Validation.Contact.cs содержит:
namespace Contacts.Entities
{
public partial class Contact : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
ContactsDbContext contacts = (ContactsDbContext)validationContext.Items["Context"];
//Check if Contact already exists with the same Name
if (contacts.Any<Contact>(c => c.Name == this.Name))
yield return new ValidationResult("Contact 'Name' is already in use.", new string[] { "Name" });
yield break;
}
}
2 ответа
Технически вы можете представить интерфейс с явной реализацией, например:
В Контактах. Собрание объектов:
public interface IContactsDbContext
{
IQueryable<Contact> Contacts { get; }
// Not DbSet<Contact> because you don't want dependency on EF assembly
}
//...
public class Contact : IValidatableObject // No partial class anymore
{
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
IContactsDbContext context =
validationContext.Items["Context"] as IContactsDbContext;
if (context.Contacts.Any<Contact>(c => c.Name == this.Name))
yield return new ValidationResult(
"Contact 'Name' is already in use.", new string[] { "Name" });
yield break;
}
// IValidatableObject, ValidationResult and ValidationContext is in
// System.ComponentModel.DataAnnotations.dll, so no dependency on EF
}
В сборке Contacts.Repository (ссылки Contacts.Entities):
public class ContactsDbContext : DbContext, IContactsDbContext
{
public DbSet<Contact> Contacts { get; set; }
IQueryable<Contact> IContactsDbContext.Contacts // explicit impl.
{
get { return Contacts; } // works because DbSet is an IQueryable
}
protected override DbEntityValidationResult ValidateEntity(
DbEntityEntry entityEntry, IDictionary<object, object> items)
{
items.Add("Context", this);
return base.ValidateEntity(entityEntry, items);
}
}
Контакты. Проверка сборки может быть удалена.
Однако мне не очень нравится это решение. Ваш POCO имеет - через Validate
Метод - все еще зависимость от хранилища, если интерфейс или нет. Для более строгого разделения интересов я, вероятно, предпочел бы иметь отдельный класс проверки, который, возможно, также выполняет операции с репо. Или если бы я реализовал IValidatableObject
Я бы, вероятно, делал только проверки, которые зависят только от свойств объекта модели (например, "дата производства не должна быть позже даты отгрузки" и т. Д.). Ну, это частично вопрос вкуса. Второй пример, который вы связали, на самом деле не заботится о разделении интересов, поэтому вы как-то конфликтуете с первым примером.
Проверка того, что определенное поле должно быть уникальным, с моей точки зрения, не проверка на уровне объекта. Это также может рассматриваться как проверка репо (репо станет недействительным, если я вставлю сущность с тем же именем).
Обычно я обращаюсь к своим репозиториям через классы обслуживания, и там я делаю "проверку" перед тем, как вставить, если уже существует объект с таким именем. Не чисто разделенная проверка. Это может стать проще и чище, если EF предоставляет функцию "Уникальные ограничения", упомянутую во втором сообщении в блоге.
этот комментарий заслуживает ответа