实体框架IValidatableObject引用DbContext

本文关键字:DbContext 引用 IValidatableObject 框架 实体 | 更新日期: 2023-09-27 18:00:26

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

我遵循本指南将我的POCO实体与EF模型进行了很好的分离,现在我正在遵循本指南实现验证(使用IValidatableObject)。

我的解决方案包括:

  • 联系人。存储库[参考EF和Contacts.Entities]:
    • 联系人.edmx
    • 联系人DbContext.cs
  • 联系人。实体[无参考文献]:
    • Contact.cs(Contacts.Entities.Contact分部类)
  • 联系人。验证[引用Contacts.Entitys和Contacts.Repository]
    • Contact.cs(Contacts.Entities.Contact分部类)

但我的验证碰壁了:

  1. 我无法将验证逻辑添加到联系人。实体,因为这会导致对Contacts的循环引用。存储库(contact.Valide(…)需要使用ContactsDbContext)。所以我创建了一个单独的联系人。验证项目
  2. 但是,这意味着将Contact类与分部类分开,以便在两个Contact中定义Contact。实体和联系人。验证。代码不再编译,因为不能在不同的程序集中定义分部类

有人给我指点迷津吗?我已经发布了下面的代码。。。

联系人。存储库。联系人DbContent.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);
    }
  }
}

联系人。实体。联系人.cs:

namespace Contacts.Entities
{
    public partial class Contact
    {
        public string Name { get; set; }
    }
}

联系人。验证。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;
      }
  }

实体框架IValidatableObject引用DbContext

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

联系人中。实体程序集:

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

联系人。验证程序集可以删除。

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

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

通常我通过服务类访问我的repos,在插入之前我会做一个"检查",看看是否已经有一个同名的实体。不是一个干净分离的验证。当EF提供第二篇博客文章中提到的"唯一约束"功能时,它可能会变得更容易、更干净。

~ Slauma Jun 28'11 17:14

这个评论应该是一个答案