17

の MSDN ドキュメントを読むことができます。

その後、そのコンテキストのモデルがキャッシュされ、アプリ ドメイン内のコンテキストの以降のすべてのインスタンス用になります。このキャッシングは、特定の ModelBuidler で ModelCaching プロパティを設定することで無効にできますが、パフォーマンスが大幅に低下する可能性があることに注意してください。

問題は、モデル ビルダーにModelCachingという名前のプロパティが含まれていないことです。

モデルのキャッシュを無効にするにはどうすればよいですか (実行時にモデルの構成を変更する場合など)。

4

4 に答える 4

4

前方警告: 異なるコンテキストからのテーブル間で結合を実行する必要がない限り、以下に示すメカニズムがニーズをカバーすることは言うまでもありません。このような操作が必要な場合は、以下に示すメカニズムを小さな API でさらに改良して、上記のテーブルを何らかの文字列または数値に動的に関連付けることができるようにする必要があります (実行時にそれぞれの DBSet に自由にアクセスして動的に結合できるようにするため)。 . この種のことを行うことは、より一般的ですが、少し複雑であり、この回答の範囲外です。

これは、bairog によって提唱されたメカニズムの本格的な実装です。すべての功績は彼にあります。コメントで説明されている理由により、新しい DbContext を介してデータベースへの接続を取得していることに注意してください。

     using System;
     using System.Collections.Concurrent;
     using System.ComponentModel.DataAnnotations;
     using System.Data.Common;
     using System.Data.Entity;
     using System.Data.Entity.Infrastructure;
     using System.Data.Entity.ModelConfiguration;

     namespace Utilities
     {
         // usage:
         //
         // var context1 = new FooContext("Schema1", "PingTable1", "PongTable1");
         // context1.Ping.Select(x => x.Id > 10).ToList();     
         // context1.Pong.Select(x => x.Id > 20).ToList();

         public class FooContext : DbContext
         {
             public DbSet<Ping> Ping { get; set; }
             public DbSet<Pong> Pong { get; set; }

             static public FooContext Spawn(string nameOrConnectionString, string pingTablename, string pongTablename, string schemaName = null) //minifactory
             {
                 //if (string.IsNullOrWhiteSpace(schemaName?.Trim())) throw new ArgumentException(nameof(schemaName)); //canbe
                 if (string.IsNullOrWhiteSpace(pingTablename?.Trim())) throw new ArgumentException(nameof(pingTablename));
                 if (string.IsNullOrWhiteSpace(pongTablename?.Trim())) throw new ArgumentException(nameof(pongTablename));

                 var dummyDbContext = new DbContext(nameOrConnectionString); //0 stupidhack for retrieving the connection

                 return new FooContext(dummyDbContext, GetModelBuilderAndCacheIt(dummyDbContext.Database.Connection, pingTablename, pongTablename, schemaName));
             }
             //0 stupidhack over EntityConnection("name=NameOfConnectionStringFromWebConfig") which wasnt working because it demands metadata on the
             //  codefirst connection to an oracle db (at least oracledb ver11 - go figure ...)
             //
             //  update: I finally had success using the *managed* driver oracle.manageddataaccess with oracle-odac ver12+    one may now use:
             //
             //  var connectionString = ConfigurationManager.ConnectionStrings[nameOrConnectionString];
             //  if (connectionString == null) return null;
             //  
             //  var factory = DbProviderFactories.GetFactory(connectionString.ProviderName);
             //  var connection = factory.CreateConnection();
             //  connection.ConnectionString = connectionString.ConnectionString; //vital
             //  
             //  new FooContext(dummyDbContext, GetModelBuilderAndCacheIt(connection, pingTablename, pongTablename, schemaName));

             private static readonly object DbCompiledModelRegistrarLocker = new object(); // ReSharper disable InconsistentlySynchronizedField
             private static readonly ConcurrentDictionary<Tuple<string, string, string>, DbCompiledModel> DbModelBuilderCache = new ConcurrentDictionary<Tuple<string, string, string>, DbCompiledModel>();

             static private DbCompiledModel GetModelBuilderAndCacheIt(DbConnection databaseConnection, string pingTablename, string pongTablename, string schemaName) //0
             {
                 var key = Tuple.Create(pingTablename, pongTablename, schemaName);
                 if (DbModelBuilderCache.ContainsKey(key))
                     return DbModelBuilderCache[key];

                 lock (DbCompiledModelRegistrarLocker)
                 {
                     if (DbModelBuilderCache.ContainsKey(key))
                         return DbModelBuilderCache[key];

                     var modelBuilder = new DbModelBuilder();
                     modelBuilder.Configurations.Add(new PingFluentConfiguration(schemaName, pingTablename));
                     modelBuilder.Configurations.Add(new PongFluentConfiguration(schemaName, pongTablename));

                     //setting a maxsize for the cache so that least used dbmodels get flushed away is left as an exercise to the reader
                     return DbModelBuilderCache[key] = modelBuilder.Build(databaseConnection).Compile();
                 }
             }

             //0 building the same model over and over is very expensive operation and this is why we resorted to caching the modelbuilders
             // ReSharper restore InconsistentlySynchronizedField

             private DbContext _dummyDbContext;

             private FooContext(DbContext dummyDbContext, DbCompiledModel compiledModel)
                 : base(dummyDbContext.Database.Connection, compiledModel, contextOwnsConnection: true)
             {
                 _dummyDbContext = dummyDbContext;

                 Database.SetInitializer<FooContext>(strategy: null); //0
             }

             //0 http://stackoverflow.com/a/39710954/863651   ef by default attempts to create the database if it doesnt exist
             //  however in this case we want ef to just do nothing if the underlying database doesnt exist

             //protected override void OnModelCreating(DbModelBuilder modelBuilder) //0 here be dragons   beware that this approach wont work as intended down the road
             //{
             //    modelBuilder.Configurations.Add(new PingFluentConfiguration(_schemaName, _tablename)); //0 here be dragons   beware that this approach wont work as intended down the road
             //    base.OnModelCreating(modelBuilder);
             //}

             protected override void Dispose(bool disposing)
             {
                 if (disposing)
                 {
                     _dummyDbContext?.Dispose();
                     _dummyDbContext = null;
                 }

                 base.Dispose(disposing);
             }
         }

         public sealed class PingFluentConfiguration : EntityTypeConfiguration<Ping>
         {
             public PingFluentConfiguration(string schemaName, string tableName)
             {
                 HasKey(t => t.Id);

                 ToTable(schemaName: schemaName, tableName: tableName);
             }
         }

         public sealed class PongFluentConfiguration : EntityTypeConfiguration<Pong>
         {
             public PongFluentConfiguration(string schemaName, string tableName)
             {
                 HasKey(t => t.Id);

                 ToTable(schemaName: schemaName, tableName: tableName);
             }
         }

         public class Ping
         {
             [Key]
             [Required]
             public string Id { get; set; }

             [Required]
             public string Name { get; set; }
         }

         public class Pong
         {
             [Key]
             [Required]
             public string Id { get; set; }

             [Required]
             public string Name { get; set; }
         }
     }
于 2017-01-31T20:20:12.263 に答える
1

ここに同様の質問があります

Entity Framework チームのプログラム マネージャー ( Rowan Miller (MSFT) ) からの唯一のアプローチは次のとおりです。

CTP5 で CacheForContextType を削除しました。当初は、異なるモデルで同じ AppDomain 内の同じコンテキストを使用したい場合に使用することを意図していました。問題は、初期化のたびにモデルを作成し、一連のモデルをキャッシュして、各初期化中に使用するモデルを選択する方法を許可しないことです。モデルの作成にはコストがかかるため、より良いパターンを宣伝したいと考えていました。

推奨するパターンは、使用するモデルごとに ModelBuilder -> DbDatabaseMapping -> DbModel を外部で作成することです。DbModel をキャッシュして、コンテキスト インスタンスの作成に使用する必要があります。ModelBuilder -> DbModel のワークフローは少し面倒で、クラス名も適切ではありません。RTM で整理される予定です。

私は次のアプローチを試しました:

  1. OnModelCreatingイベント ハンドラー内にあったすべての操作を、 DbModelBuilderを作成する新しい関数に移動します ( DbConnectionをパラメーターとしてこの関数に渡す可能性が高くなります)。
  2. DbModelBuilder.Build(DbConnecton)経由でDbModel を取得します。
  3. DbModel.Compile ()を介してDbCompiledModel を取得します。
  4. パラメーター(DbConnection、DbCompileModel、bool)を使用してDbContextの新しいコンストラクターを作成し、その中に以前に作成したDbCompiledModelを渡します。

その結果、DbContextコンストラクターを呼び出すたびにDbCompiledModelのパラメーターを変更できるようになりました。必要なのはそれだけでした。

于 2016-11-23T04:43:09.670 に答える