对于一个简短的问题,这是一个很长的介绍,抱歉!!
我正在使用EF 4.3.1 Code First,我有以下模型
public class Action
{
protected Action()
{ }
public virtual int ActionID { get; protected set; }
[Required]
[StringLength(DataValidationConstants.NameLength)]
public virtual string Name {get; set;}
[StringLength(DataValidationConstants.DescriptionLength)]
public virtual string Description { get; set; }
public virtual ICollection<Role> Roles { get; set; }
public virtual void AuthorizeRole(Role role)
{
if (IsRoleAuthorized(role))
throw new ArgumentException("This role is already authorized", "role");
Roles.Add(role);
}
}
public class Role
{
protected Role()
{ }
public virtual int RoleID { get; protected set; }
[Required]
[StringLength(DataValidationConstants.NameLength)]
public virtual string Name { get; set; }
[StringLength(DataValidationConstants.DescriptionLength)]
public virtual string Description { get; set; }
}
我的DBContext类,在其他类库中定义,具有多对多的映射:
public class myDB : DbContext
{
public DbSet<Domain.Action> Actions { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Domain.Action>()
.HasMany(action => action.Roles)
.WithMany()
.Map(map => map.ToTable("AuthorizedRoles"));
}
}
所以,这很好用。但是如果注意到方法Action.AuthorizeRole(Role role)
很容易假设角色授权逻辑可能很复杂(现在已经有一些已经授权的验证,但可能是任何验证,对吗?),这是完全有效的事情。一个很好的老式领域模型。但是......根据Requirements for Creating POCO Proxies,角色集合public virtual ICollection<Role> Roles {get; set;}
需要公开,至少是吸气者。这意味着Action类的任何客户端都可以绕过任何验证逻辑来添加或删除角色。
是的,我想要延迟加载,更改跟踪,工作,所以我确实需要创建代理。
Regardlles,我开始测试一些方法,使我能够使这个属性public virtual ICollection<Role> Roles {get; set;}
成为非公共属性,以便稍后测试代理创建。由于代理生成了我自己的类的子类,并且因为我相信我的继承者而不是我的客户,所以我决定将属性protected
设为protected virtual ICollection<Role> Roles {get; set;}
。但是,当然,我在行上遇到了编译错误
modelBuilder.Entity<Domain.Action>()
.HasMany(action => action.Roles)
.WithMany()
.Map(map => map.ToTable("AuthorizedRoles"));
因为现在该属性受到保护,无法在Action类或其继承者之外访问,当然myDB
上下文类不是其中之一。
因此我需要尝试从myDB
类访问该属性,而不将其(属性)公开。我虽然反思。我的上下文类看起来像这样:
public class myDB : DbContext
{
public DbSet<Domain.Action> Actions { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Domain.Action>()
.HasMany(action => ExtractPropertyValue<ICollection<Role>>(action, "Roles"))
.WithMany()
.Map(map => map.ToTable("AuthorizedRoles"));
}
protected virtual TProperty ExtractPropertyValue<TProperty>(object instance, string propertyName)
{
if(instance == null)
throw new ArgumentNullException("instance", "Can't be null");
if (string.IsNullOrWhiteSpace(propertyName))
throw new ArgumentException("Can't be null or white spaced", "propertyName");
Type instanceType = instance.GetType();
PropertyInfo propertyInfo = instanceType.GetProperty(propertyName, BindingFlags.NonPublic);
return (TProperty)propertyInfo.GetValue(instance, null);
}
}
注意新方法ExtractPropertyValue
,在多对多映射指令上调用它。这样可行吗? HasMany方法期望一个函数接收一个Action并返回一个ICollection的东西(在这种情况下是Role),这就是得到的东西。但不,它不起作用,它编译了courrse,但在运行时,我得到了异常,就像“这个expresion必须是一个有效的属性,如obj =&gt; obj.MyProperty”。
好吧,它需要是一个“直接”属性,它需要可以访问de DBContext类。我决定将我的属性设置为protected internal并将我的DBContext类移动到我的Domain类库(其中定义了所有实体),我真的不喜欢那么多,但是我更喜欢让我的属性可以被所有人访问。我的财产看起来像这样:
protected internal virtual ICollection<Role> Roles { get; set; }
我的DBContext类就像我第一次拥有它一样,只是现在它定义在与所有实体相同的类库中:
public class mrMantisDB : DbContext
{
public DbSet<Domain.Action> Actions { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Domain.Action>()
.HasMany(action => action.Roles)
.WithMany()
.Map(map => map.ToTable("AuthorizedRoles"));
}
}
这样可以正常工作。 因此,现在唯一需要检查的是创建代理,即延迟加载和更改跟踪。作为财产保护内部而不是公共我害怕它可能不起作用,但是它确实,所有这一切,并且顺利,真的。
现在,这是我的问题/要求。如果导航属性实际上不需要公开来创建代理,那么受保护就足够了(因为我假设它只会影响使用该属性进行关系映射的能力),为什么在地球上限制在表达式上提取HasMany方法的属性,或者更好,因为我理解属性必须是被映射类型的属性而不是一些随机集合,为什么HasMany没有重载,它接受字符串propertyName和即使它不是公共财产,也要搜索该物业。这将允许非公共导航属性,在我看来,它一直是允许一个整洁的设计对象domian。
也许我在这里错过了什么。
Thanx很多。
答案 0 :(得分:1)
你的问题问为什么对模型构建者的受保护属性的限制,我不确定为什么会这样。但是,如果您想要解决方法,我已成功实施this blog的解决方案。
您将使用表达式更新您的实体,以便模型构建者可以找到它:
protected virtual ICollection<Role> Roles { get; set; }
public class PropertyAccessExpressions
{
public static readonly Expression<Func<User, ICollection<Role>>> ID = x => x.Roles;
}
然后你的建造者应该能够找到这个:
modelBuilder.Entity<Domain.Action>()
.HasMany(action => action.Roles)