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)と、役割の承認ロジックが複雑である可能性があると簡単に推測できます (現在は承認済みの検証がいくつかありますが、任意の検証である可能性がありますよね?)。モデル。しかし... Requirements for Creation POCO Proxiespublic virtual ICollection<Role> Roles {get; set;}によると、少なくとも getter はロール コレクションを公開する必要があります。つまり、Action クラスのクライアントは、検証ロジックをバイパスしてロールを追加または削除できます。はい、遅延読み込み、変更追跡、動作が必要なので、プロキシを作成する必要があります。

public virtual ICollection<Role> Roles {get; set;}よろしくお願いします。後でプロキシの作成をテストするために、このプロパティを非パブリック プロパティにする方法をいくつかテストすることにしました。プロキシが生成したサブクラスは自分のクラスであり、継承者は信頼するがクライアントは信頼しないため、プロパティprotectedを so のようにすることにしましたprotected virtual ICollection<Role> Roles {get; set;}。しかし、もちろん、次の行でコンパイルエラーが発生しました

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

現在、プロパティは保護されており、Action クラスまたはその継承者の外部ではアクセスできず、確かにmyDBコンテキスト クラスはそれらの 1 つではないためです。

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

new methodExtractPropertyValueに注意してください。多対多マッピング命令での呼び出しです。これは正しく機能しますか?HasMany メソッドは、Action を受け取り、何か (この場合は Role) の ICollection を返す関数を期待しており、それが取得されています。しかし、いいえ、動作しません。もちろんコンパイルされますが、実行時に、「この式は obj => obj.MyProperty のような有効なプロパティでなければなりません」のような例外が発生しました。

わかりました、それは「直接的な」プロパティである必要があり、DBContext クラスにアクセスできる必要があります。プロパティを protected internal に設定し、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"));
     }
}

そして、これは問題なく動作します。したがって、チェックする必要があるのは、プロキシの作成、つまり、遅延読み込みと変更の追跡だけです。プロパティがパブリックではなく内部で保護されているため、機能しないのではないかと心配していましたが、すべてスムーズに機能しました。

では、質問・お願いです。プロキシを作成するためにナビゲーション プロパティを実際にパブリックにする必要がない場合は、protected で十分です (関係マッピングにそのプロパティを使用する機能にのみ影響すると想定しているため、internal out を省略しています)。 HasMany メソッドのプロパティを抽出する式、またはさらに良いことに、プロパティはマップされているタイプのプロパティである必要があり、ランダムなコレクションではないことを理解しているため、文字列 propertyName を受け取る HasMany のオーバーロードがないのはなぜですかパブリック プロパティでなくても、プロパティ自体を検索します。これにより、非パブリック ナビゲーション プロパティを使用できるようになります。これは、私の観点では、きちんとデザインされたオブジェクト ドミアンを可能にします。

多分私はここで何かを逃しています。

どうもありがとう。

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 に答える