Entity Framework IValidatableObject ссылка DbContext - PullRequest
6 голосов
/ 27 июня 2011

Я пытаюсь заставить 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 частичный класс)

Но я бью кирпичную стену с проверкой:

  1. Я не могу добавить логику проверки в Контакты.Entities, потому что это вызвало бы циклическую ссылку с Contacts.Repository (contact.Validate (...) должен использовать ContactsDbContext).Поэтому я создал отдельный проект Contacts.Validation.
  2. Но это означает разделение класса 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 ]

7 голосов
/ 27 июня 2011

Технически вы могли бы представить интерфейс с явной реализацией, например:

In Contacts.Entities сборка:

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
}

In Contacts.Repository сборка (ссылки Contacts.Entities Assembly):

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);
    }
}

Contacts.Validation сборка может быть удалена.

Однако мне не очень нравится это решение.Ваш POCO имеет - через метод Validate - все еще зависимость от хранилища, если интерфейс или нет.Для более строгого разделения интересов я, вероятно, предпочел бы иметь отдельный класс проверки, который, возможно, также выполняет операции с репо.Или, если бы я реализовал IValidatableObject, я бы, вероятно, делал только проверки, которые зависят только от свойств объекта модели (например, «дата производства не должна быть позже даты отгрузки» и т. Д.).Ну, это частично вопрос вкуса.Второй пример, который вы связали, на самом деле не заботится о разделении интересов, поэтому вы как-то конфликтуете с первым примером.

1 голос
/ 06 июня 2016

Проверка того, что конкретное поле должно быть уникальным, с моей точки зрения, не проверка на уровне сущность . Это также может рассматриваться как проверка репо (репо станет недействительным, если я вставлю сущность с тем же именем).

Обычно я обращаюсь к своим репозиториям через классы обслуживания, и там я делаю "проверку", прежде чем вставлять, если уже существует сущность с таким именем. Не чисто разделенная проверка. Это может стать проще и чище, если EF предоставляет функцию «Уникальные ограничения», упомянутую во втором сообщении в блоге.

~ Слаума 28 июня 11 в 17: 14

этот комментарий заслуживает ответа

...