EntityFramework 4.3.1代码优先导航属性映射和可见性 [英] EntityFramework 4.3.1 Code First Navigation Property Mapping and Visibility

查看:86
本文介绍了EntityFramework 4.3.1代码优先导航属性映射和可见性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This is a long introduction for a short question, sorry!!

我正在使用EF 4.3.1 Code First,并且具有以下模型

I'm working with EF 4.3.1 Code First and I've got the following model

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类(在其他类库中定义)具有多对多映射:

And my DBContext class, defined in some other class library, with many to many mapping:

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)很容易假设角色授权逻辑可能很复杂(现在已经有些授权验证,但是可以是任何验证,对吧?),这完全是一件有效的事情.老式的域模型.但是,根据

So, this works alright. But if noticing the method Action.AuthorizeRole(Role role) is easy to asume that the Role Authorization logic might be complex (some already-authorized-validation now, but could be any validation, right?), which is a completely valid thing to have on a good old fashioned domain model. But... the Roles collection public virtual ICollection<Role> Roles {get; set;} needs to be public, the getter at least, according to Requirements for Creating POCO Proxies. That means that any client of the Action class could add or remove roles, bypassing any validation logic. And yes, I want lazy loading, change tracking, the works, so I do need proxies to be created.

Regardlles,我着手测试一些使该属性public virtual ICollection<Role> Roles {get; set;}成为非公共属性的方法,以便以后测试代理的创建.由于代理生成了我自己的类的子类,并且由于我信任继承人但不信任我的客户,因此我决定像protected virtual ICollection<Role> Roles {get; set;}那样使属性protected.但是,当然,我在网上遇到了编译错误

Regardlles, I set out to test some ways in which I'd be able to make this property public virtual ICollection<Role> Roles {get; set;} a non public property, to later test for proxy creation. As the proxies generated subclass my own class, and as I trust my inheritors but not my clients, I decide to make the property protected like so protected virtual ICollection<Role> Roles {get; set;}. But then, of course, I got a compilation error on the line

modelBuilder.Entity<Domain.Action>()
            .HasMany(action => action.Roles)
            .WithMany()
            .Map(map => map.ToTable("AuthorizedRoles"));

因为现在该属性已受保护,无法在Action类或其继承者之外访问,所以myDB上下文类当然不是其中之一.

because now the property is protected and can't be accessed outside Action class or its inheritors, and certainly myDB context class is not one of those.

因此,我需要尝试从myDB类访问该属性,而不要将该属性(该属性)公开.而我虽然有所思索.我的上下文类如下:

So I needed to try and access the property from myDB class without it (the property) being made public. And I though of reflection. My context class looked like this then:

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,但是在运行时,出现了类似此表达式必须具有obj => obj.MyProperty之类的有效属性"之类的异常.

Notice the new method ExtractPropertyValue, ant the call to it on the many to many mapping instruction. This sould work right? The HasMany method is expecting a function that receives an Action and returns an ICollection of something (Role in this case) and that's what is getting. But no, it does not work, it compiles of courrse, but on runtime, I got and exception that was something like "This expresion must be of a valid property like obj => obj.MyProperty".

好吧,它必须是一个直接"属性,并且必须是DBContext类可访问的.我决定将属性设置为受保护的内部属性,并将DBContext类移动到我的Domain类库(定义了所有实体的域)中,我确实不太喜欢这种方法,但是我更喜欢让每个人都可以使用我的属性.我的财产看起来像这样:

So ok, it needs to be a "direct" property and it needs to be accesible to de DBContext class. I decided to set my property to protected internal and to move my DBContext class to my Domain class library (where all the entities are defined), which I really dont like that much, but which I liked better thant having my property being accesible by everyone. My property looked like this:

protected internal virtual ICollection<Role> Roles { get; set; }

我最初拥有的DB​​Context类就是这样,只是现在它与所有实体都在同一个类库中定义了:

And my DBContext class like this, exactly as I first had it, only that it is now defined in the same class library as all the entities are:

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

这行得通. 因此,现在唯一需要检查的是代理的创建,即具有延迟加载和更改跟踪.作为内部而不是公共场所的财产保护,我担心这可能不起作用,但是的,它确实起作用了,而且一切顺利,而且确实如此.

And this works all right. So, now the only thing left to check was the creation of proxies, that is, having lazy loading and change tracking. Being the property protected internal instead of public I was afraid it might not work, but yes it did, all of it, and smoothly, really.

现在,这是我的问题/要求.如果实际上不需要为创建代理而将导航属性公开,那么受保护就足够了(我不做内部解释,因为我认为这只会影响使用该属性进行关系映射的能力),这到底是为什么呢?在表达式中提取HasMany方法的属性,或者更好,因为我知道该属性必须是要映射的类型的属性,而不是一些随机集合,所以为什么不存在HasMany的重载,它需要一个字符串propertyName和即使本身不是公共财产,也要搜索财产本身.这将允许具有非公共导航属性,以我的观点,这将一直允许一个整洁的设计对象domian.

Now, here comes my question/request. If a navigation property does not really needs to be public for proxies to be created, protected is enough (I'm leaving internal out because I assume that only influences the ability to use that property for the relation mapping), why on earth the restriction on the expression to extract the property for the HasMany method, or better yet, since I understand the property must be a property of the type being mapped and not some random collection, why is not there an overload for HasMany which takes a string propertyName and searches the property for itself, even if it is not a public property. This would allow to have non public navigation properties, which in my point of view, go all the way to allow a neatly design object domian.

也许我在这里丢失了一些东西.

Maybe I'm missing something here.

非常感谢.

推荐答案

您的问题问为什么对Modelbuilder的受保护属性进行限制,我不确定为什么会这样.但是,我已经成功实现了

Your question asked why the restriction on the protected property for the modelbuilder,and I'm not sure why that's the case. However, I've had success implementing a solution from this blog if you're wanting a workaround.

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

You would update your entity with an expression so the modelbuilder can find it:

  protected virtual ICollection<Role> Roles { get; set; }

        public class PropertyAccessExpressions
        {
            public static readonly Expression<Func<User, ICollection<Role>>> ID = x => x.Roles;
        }

那么您的构建者应该可以找到这个:

Then your builder should be able to find this:

  modelBuilder.Entity<Domain.Action>()
            .HasMany(action => action.Roles)

这篇关于EntityFramework 4.3.1代码优先导航属性映射和可见性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆