0

这是一个简短问题的长篇介绍,对不起!

我正在使用 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)很容易假设角色授权逻辑可能很复杂(现在一些已经授权的验证,但可能是任何验证,对吧?),这在一个好的老式域上是完全有效的模型。但是...根据创建 POCO 代理的要求,角色集合public virtual ICollection<Role> Roles {get; set;}需要公开,至少是吸气剂。这意味着 Action 类的任何客户端都可以添加或删除角色,绕过任何验证逻辑。是的,我想要延迟加载、更改跟踪等工作,所以我确实需要创建代理。

问候,我开始测试一些方法,使我能够将此属性设为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 类或其继承者之外访问,当然myDBcontext 类不是其中之一。

所以我需要尝试在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),这就是得到的。但是不,它不起作用,它当然可以编译,但是在运行时,我得到了类似“这个表达式必须是像 obj => obj.MyProperty 这样的有效属性”的异常。

好吧,它需要是一个“直接”属性,并且它需要可以访问 de DBContext 类。我决定将我的属性设置为受保护的内部并将我的 DBContext 类移动到我的域类库(其中定义了所有实体),我真的不太喜欢它,但我更喜欢让我的属性被所有人访问. 我的财产看起来像这样:

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 和搜索财产本身,即使它不是公共财产。这将允许具有非公共导航属性,在我看来,这将一直允许一个整洁设计的对象域。

也许我在这里遗漏了一些东西。

非常感谢。

4

1 回答 1

1

您的问题询问为什么对模型构建器的受保护财产进行限制,我不确定为什么会这样。但是,如果您想要解决方法,我已经成功地实施了这个博客中的解决方案。

您将使用表达式更新您的实体,以便模型构建器可以找到它:

  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)
于 2012-08-03T16:23:44.667 回答