8

我正在尝试让 EF 4.1 与 Repository、UnitOfWork、实体与 EF 的分离和验证一起工作。

我按照指南将我的 POCO 实体与 EF 模型很好地分离,现在我正在按照指南实施验证(使用 IValidatableObject)。

我的解决方案包括:

  • Contacts.Repository [引用 EF 和 Contacts.Entities]:
    • 联系人.edmx
    • ContactsDbContext.cs
  • Contacts.Entities [无引用]:
    • Contact.cs(Contacts.Entities.Contact 部分类)
  • Contacts.Validation [引用 Contacts.Entities 和 Contacts.Repository]
    • Contact.cs(Contacts.Entities.Contact 部分类)

但是我在验证中遇到了障碍:

  1. 我无法向 Contacts.Entities 添加验证逻辑,因为它会导致使用 Contacts.Repository 进行循环引用(contact.Validate(...) 需要使用 ContactsDbContext)。所以我创建了一个单独的 Contacts.Validation 项目。
  2. 但是,这意味着将 Contact 类与部分类分开,以在 Contacts.Entities 和 Contacts.Validation 中定义 Contact。代码不再编译,因为您无法跨不同的程序集定义分部类。

有人在这里给我指点吗?我已经在下面发布了代码...

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

2 回答 2

10

从技术上讲,您可以引入一个具有显式实现的接口,如下所示:

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
}

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

可以删除Contacts.Validation程序集。

但是,我不太喜欢这种解决方案。您的 POCO - 通过该Validate方法 - 仍然依赖于存储库,无论是否接口。为了更好地分离关注点,我可能更喜欢有一个单独的 Validation 类,它可能也对 repo 进行操作。或者,如果我要实现IValidatableObject,我可能只会进行仅依赖于模型对象属性的验证(例如“生产日期不得晚于发货日期”等)。嗯,这部分是口味问题。您链接的第二个示例并不真正关心关注点分离,因此您与第一个示例存在某种冲突。

于 2011-06-27T18:25:05.260 回答
1

从我的角度来看,特定字段必须唯一的验证不是实体级别的验证。它也可以被视为对 repo 的验证(如果我插入同名的实体,repo 将变得无效)。

通常我通过服务类访问我的存储库,如果已经存在同名实体,我会在插入之前进行“检查”。不是一个完全分离的验证。当 EF 提供第二篇博文中提到的“唯一约束”功能时,它可能会变得更容易和更清晰。

~ Slauma 2011 年 6 月 28 日 17:14

这个评论应该是一个答案

于 2016-06-06T09:42:59.680 回答