实体框架IValidatableObject参考DbContext

Mat*_*att 6 validation entity-framework-4.1 dbcontext

我正在尝试让EF 4.1与Repository,UnitOfWork一起工作,将实体与EF分离并进行验证.

我按照指南将我的POCO实体从EF模型中分离出来,现在我按照指南实现验证(使用IValidatableObject).

我的解决方案包括:

  • Contacts.Repository [引用EF和Contacts.Entities]:
    • Contacts.edmx
    • ContactsDbContext.cs
  • Contacts.Entities [无参考]:
    • Contact.cs(Contacts.Entities.Contact partial class)
  • Contacts.Validation [引用Contacts.Entities和Contacts.Repository]
    • Contact.cs(Contacts.Entities.Contact partial class)

但我正在用验证打砖墙:

  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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Contacts.Entities.Contact.cs:

namespace Contacts.Entities
{
    public partial class Contact
    {
        public string Name { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
      }
  }
Run Code Online (Sandbox Code Playgroud)

Sla*_*uma 8

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

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
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Contacts.Validation程序集可以删除.

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