实体框架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分部类)
但我的验证碰壁了:
- 我无法将验证逻辑添加到联系人。实体,因为这会导致对Contacts的循环引用。存储库(contact.Valide(…)需要使用ContactsDbContext)。所以我创建了一个单独的联系人。验证项目
- 但是,这意味着将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;
}
}
从技术上您可以引入一个具有显式实现的接口,例如:
在联系人中。实体程序集:
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
这个评论应该是一个答案