实体框架IValidatableObject参考DbContext

时间:2011-06-27 15:23:28

标签: validation entity-framework-4.1 dbcontext

我试图让EF 4.1与Repository,UnitOfWork合作,将实体与EF分离并进行验证。

我按照this指南将我的POCO实体与EF模型分开,我现在按照this指南实现验证(使用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。代码不再编译,因为您无法在不同的程序集中定义部分类。
  3. 有人在这里有任何指示吗?我已经发布了以下代码......

    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 个答案:

答案 0 :(得分:7)

技术上你可以引入一个具有明确实现的接口,如下所示:

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,我可能只会进行仅依赖于模型对象属性的验证(例如“生产日期不得晚于发货日期”等等)。嗯,这部分是品味问题。您链接的第二个示例并不关心问题的分离,因此您与第一个示例存在某种冲突。

答案 1 :(得分:1)

特定字段必须唯一的验证来自我的观点,而不是实体级别的验证。它也可以被视为对repo的验证(如果我插入一个具有相同名称的实体,则repo将变为无效)。

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

Slauma Jun 28 '11 at 17:14

此评论值得回答