2

私はMVCとEFを初めて使用します。私のアプリは単純なコードです-最初にいくつかのPOCOクラスと次のようなDBContextがあります:

   public class ExpDefContext : DbContext
   {
      public DbSet<Experiment> Experiments { get; set; }
      public DbSet<Research> Researches { get; set; }
      ...

問題:データモデルにエンティティセットを追加する必要があります。そのタイプは実行時にユーザー入力から作成されます。つまり、データ構造がわかりません。

非ジェネリックDbsetクラスはこのためだけに作成されていることを読んだので、コンテキストに追加しました。

    public DbSet Log { get; set; }

...そして、runtime-typeを受け入れ、新しいDbsetを設定するコンテキストのコンストラクターを作成しました。

        public ExpDefContext(Type LogRecType)
        {
            Log = Set(LogRecType);
        }

(ちなみにタイプはReflection.Emitを使用して作成されています)。

コントローラでタイプ(LogRecという名前)を作成し、それを新しいDBContextインスタンスに渡します。次に、LogRecインスタンスを作成し、それをデータベースに追加しようとします。

    Type LogRec;
    LogRec = LogTypeBuilder.Build(dbExpDef, _experimentID);
    var dbLog = new ExpDefContext(LogRec);
    var testRec = LogRec.GetConstructor(Type.EmptyTypes).Invoke(Type.EmptyTypes);
    dbLog.Log.Add(testRec);
    dbLog.SaveChanges();

そして、dbLog.Log.Add(testRec)から例外が発生します。

エンティティタイプLogRecは、現在のコンテキストのモデルの一部ではありません

私は何が間違っているのですか?これを行うためのより良い方法はありますか(できればEntity Frameworkを深く掘り下げずに)?

ありがとう

4

1 に答える 1

4

EFはDbSet<T>、派生した一般的なプロパティのみを反映し、モデルがメモリに作成されるときにDbContext非一般的なプロパティを無視するのではないかと思います。DbSet

ただし、別のアプローチとして、Fluent APIを使用してOnModelCreating、動的タイプをエンティティとしてモデルに追加することもできます。

まず、AppDomainが初めてロードされたときにモデルがメモリに組み込まれている場合にのみ、モデルにタイプを追加できます。(モデルはAppDomainごとに1回だけビルドされます。)オーバーロードされたコンストラクターに加えてコンテキストのデフォルトコンストラクターがあり、このデフォルトコンストラクターを使用してコンテキストインスタンスを作成して使用した場合、モデルは静的タイプとAppDomainが存続している限り、動的タイプをエンティティとして使用することはできなくなります。それはあなたが持っている例外を正確にもたらすでしょう。

考慮すべきもう1つのポイントは、データベーススキーマの作成です。コンパイル時にタイプが不明な場合、データベーススキーマはコンパイル時に不明です。アプリケーションの次回の実行時に新しいタイプが原因でモデルが変更された場合は、データベースを最初から再作成するか、テーブルを削除しLogRecて新しいテーブルを作成するだけのカスタムデータベース初期化子を定義することにより、データベーススキーマを更新する必要があります。タイプの新しいレイアウトに応じてLogRec。あるいは、コードファーストの移行が役立つかもしれません。

Fluent APIで可能な解決策について:

を削除し、代わりにコンテキストにメンバーDbSetを追加してTypeオーバーライドしますOnModelCreating

public class ExpDefContext : DbContext
{
    private readonly Type _logRecType;

    public ExpDefContext(Type LogRecType)
    {
        _logRecType = LogRecType;
    }

    public DbSet<Experiment> Experiments { get; set; }
    public DbSet<Research> Researches { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
        entityMethod.MakeGenericMethod(_logRecType)
            .Invoke(modelBuilder, new object[] { });
    }        
}

DbModelBuilder非ジェネリックEntityメソッドがないため、ジェネリックメソッドを動的に呼び出すEntity<T>必要があります。

上記のコードOnModelCreatingは、...の動的な対応物です。

modelBuilder.Entity<LogRec>();

...これは静的LogRecタイプで使用され、EFにエンティティとしてのタイプを認識させるだけです。DbSet<LogRec>これは、コンテキストクラスにプロパティを追加するのとまったく同じです。

を使用して、動的エンティティのエンティティセットにアクセスできる必要があります...

context.Set(LogRecType)

...これは非ジェネリックを返しDbSetます。

それがうまくいくかどうかはわかりませんが、テストはしていませんが、EFチームのメンバーであるRowan Millerからのアイデアなので、うまくいくことを期待しています。

于 2012-09-29T14:34:17.397 に答える