2012-03-03 11 views
0

マルチテナントアプリケーションで使用するEF 4.2 EDMXモデルがあります。私は、同じEDMモデルを使用する約100のデータベースに接続します。初めて各データベースにアクセスすると、私のワーキングセットは〜12Mb上がります。これは主にEDMメタデータキャッシュによって行われるようです。メモリ使用量は元に戻ることはありません。私は同じモデルであるので、メタデータ/クエリキャッシュを共有できると思います。エンティティフレームワーク共有モデルを使用したメモリ使用

私はメモリフットプリントを減らすための提案を探していますが、私はこれを支配していないと思われます。

注:この同じシナリオはCodeFirst(これも同様に使用しています)では問題ありませんが、まだEDMXモデルを使用していて、今すぐ変換することができない多くのコードがあります。

ありがとうございます!

あなたはClearCache()メソッドを使用することができます。

+0

トラッキング対ノートラッキングの効果を見ましたか?それとも、常にトラッキングが必要ですか? –

+0

私たちは決して追跡する必要はありませんが、それがオンであるかどうかには違いはありません。とにかくGCを実行すると、そのような記憶を整理すべきだと思います。 – jlew

答えて

5

自分でMetadataWorkspaceをキャッシュすることで、自分が望むものが得られると思います。これは基本的に、コードファーストを使用するときにDbContextが内部的に行うことです。それは簡単ではありませんが、動作するはずの素早いプロトタイプを作成しました。

ここでの基本的な考え方は、EFにMetadataWorkspaceを一度作成させてからキャッシュし、コンテキストインスタンスを作成する必要があるたびに明示的に使用させることです。これは明らかに、各コンテキストインスタンスが同じモデルを使用している場合、つまり、同じEDMX。

public class SingleModelCachingObjectContext : ObjectContext 
{ 
    private static readonly object WorkspaceLock = new object(); 
    private static MetadataWorkspace _workspace; 

    public SingleModelCachingObjectContext(string connectionStringName) 
     : base(CreateEntityConnection(connectionStringName)) 
    { 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      ((EntityConnection)Connection).StoreConnection.Dispose(); 
     } 
    } 

    private static EntityConnection CreateEntityConnection(string connectionStringName) 
    { 
     lock (WorkspaceLock) 
     { 
      if (_workspace == null) 
      { 
       _workspace = new EntityConnection("name=" + connectionStringName).GetMetadataWorkspace(); 
      } 
     } 

     var builder = 
      new DbConnectionStringBuilder 
      { 
       ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString 
      }; 

     var storeConnection = DbProviderFactories.GetFactory((string)builder["provider"]).CreateConnection(); 
     storeConnection.ConnectionString = (string)builder["provider connection string"]; 

     return new EntityConnection(_workspace, storeConnection); 
    } 
} 

あなたがそのようにのようなあなたのDbContextクラスにコンストラクタを作成することで、これを使用します:

public MyDbContext(string connectionStringName) 
    : base(new SingleModelCachingObjectContext(connectionStringName), 
      dbContextOwnsObjectContext: true) 
{ 
} 

これは、それがどのように動作するかであるこの作品を作るために、私は、キャッシングを扱う派生のObjectContextを作成しました。 DbContextのインスタンスを作成すると、使用するEF接続文字列の名前で渡されるSingleModelCachingObjectContextのインスタンスが作成されます。また、DbContextには、DbContextが配置されているときにこのObjectContextを破棄するように指示されます。

SingleModelCachingObjectContextでは、EF接続文字列を使用してMetadataWorkspaceを作成し、一度作成した静的フィールドにキャッシュします。これは非常にシンプルなキャッシングであり、ロック付きの単純なスレッドセーフです。アプリのニーズに合わせて自由に設定できます。

MetadataWorkspaceを取得すると、EF接続文字列が解析され、ストア接続文字列とプロバイダが取得されます。これは、通常のストア接続を作成するために使用されます。

ストア接続とキャッシュされたMetadataWorkspaceを使用してEntityConnectionを作成し、次に通常のキャッシュメカニズムを使用する代わりにキャッシュされたMetadataWorkspaceを使用するObjectContextを作成します。

このObjectContextは、DbContextを後退させるために使用されます。 Disposeメソッドは、ストア接続がリークしないようにオーバーライドされます。 DbContextが配置されるとObjectContextが破棄され、Disposeが呼び出され、ストア接続が破棄されます。

私はそれが実際に実行されていることを確認する以外にこれをテストしていません。それが本当にあなたのメモリ使用の問題に役立つかどうかを知ることは非常に興味深いでしょう。

+0

これをテストしたところ、実際に問題は解決しました。ありがとう! – jlew

0

私はあなたの問題ではなく私の頭の上のいくつかの提案で多くの経験を持っていませんか?

も使用してみてください:あなたはEdmMetadataテーブルを削除する場合は、データベース・スキーマがモデルと一致するかどうかをチェックするものがないことを

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Conventions.Remove<IncludeMetadataConvention>(); 
} 

注意。

DbContextテンプレートやPoco生成テンプレート(http://blogs.msdn.com/b/adonet/archive/2010/01/25/walkthrough-poco-template-for- the-entity-framework.aspx)。

よろしくお願いいたします。

+0

これはコードのためのものです。 – jlew

+0

事前生成ビューはDatabaseFirstで使用されており、この方法を使用することをお勧めします(詳細はhttp://msdn.microsoft.com/en-us/library/bb896240.aspx)POCOエンティティジェネレータhttp:// visualstudiogallery .msdn.microsoft.com/23df0450-5677-4926-96cc-173d02752313またはEFを更新した場合はDbContext Generatorを使用します。 –

+0

私はこれらのことを試すことができましたが、リンクの中には何もメモリの問題で私を助けるものはありません。 – jlew

0

私はアーサーによって与えられた答えに追加しています。 Arthurが提示したソリューションを使用しているときに問題が発生しました。私はEFモデルにマップされたストアドプロシージャのカップルを持っていました。実行していたときに、それは次のメッセージで失敗していました。

のSystem.InvalidOperationExceptionがキャッチされた メッセージを=「EntityCommand.CommandTextの値がのStoredProcedureコマンドに対して有効ではありません。EntityCommand.CommandText値がフォームのContainerName.FunctionImportName "でなければなりません。」 Source: "System.Data.Entity"

これは、MetadataWorkspaceを使用してコンテキストを初期化するときにDefaultContainerNameが設定されていないために発生していました。これが正しく動作するために、私は以下の変更を加えました。

マルチテナントDBの場合は、コンフィグレーションで接続文字列を読み込んで実行時に渡すことはないため、フォーム設定を読み込む代わりに、EFConnectionを使用する方法が少し異なります。また、さまざまなコンテキスト間で実装を共有できるようにジェネリックを使用しました。また、ワークスペースが設定されている場合にのみ、スレッドをロックするように実装を変更します。

public class SingleModelCachingObjectContext<T> : ObjectContext 
{ 
    private static readonly object WorkspaceLock = new object(); 
    private static MetadataWorkspace _workspace; 

    public SingleModelCachingObjectContext(string connectionString) 
     : base(CreateEntityConnection(connectionString)) 
    { 
     DefaultContainerName = typeof (T).Name; 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      ((EntityConnection)Connection).StoreConnection.Dispose(); 
     } 
    } 

    protected static EntityConnection CreateEntityConnection(string connectionString) 
    { 
     if (_workspace == null) 
     { 
      lock (WorkspaceLock) 
      { 
       _workspace = new EntityConnection(connectionString).GetMetadataWorkspace(); 
      } 
     } 

     var builder = 
      new DbConnectionStringBuilder 
      { 
       ConnectionString = connectionString 
      }; 

     var storeConnection = DbProviderFactories.GetFactory((string)builder["provider"]).CreateConnection(); 
     storeConnection.ConnectionString = (string)builder["provider connection string"]; 
     return new EntityConnection(_workspace, storeConnection); 
    } 
} 
関連する問題