12

seguito MSDN documentation possiamo leggere:Come disattivare modello di memorizzazione nella cache in Entity Framework 6 (codice Primo approccio)

Il modello per tale contesto viene poi memorizzato nella cache ed è per tutte le ulteriori istanze del contesto nel dominio app . Questa memorizzazione nella cache può essere disabilitata impostando la proprietà ModelCaching sul ModelBuidler specificato, ma si noti che ciò può seriamente peggiorare le prestazioni.

Il problema è il costruttore del modello non contiene alcuna proprietà denominata ModelCaching.

Come è possibile disabilitare la cache del modello (ad esempio per modificare la configurazione del modello in un tempo di esecuzione)?

+0

Spero che tu possa accettare la risposta qui sotto. – Sampath

+0

Ho affrontato lo stesso problema: hai trovato qualche soluzione? – bairog

+0

Purtroppo no. Ho cambiato il mio approccio nel progetto quindi non è stato necessario approfondire questo problema. – Kryszal

risposta

1

Here è simile domanda

L'unico approccio a disposizione deriva dal Program Manager del team di Entity Framework (Rowan Miller (MSFT)):

Abbiamo rimosso CacheForContextType in CTP5, inizialmente intendevamo utilizzarlo quando la gente voleva utilizzare lo stesso contesto nello stesso AppDomain con modelli diversi. Il problema è che creerebbe il modello su ogni inizializzazione e non permetteva alcun modo di memorizzare nella cache una serie di modelli e scegliere quale utilizzare durante ogni inizializzazione. La creazione del modello è costosa quindi volevamo promuovere un modello migliore.

Lo schema che si consiglia di creare esternamente un ModelBuilder -> DbDatabaseMapping -> DbModel per ogni modello che si desidera utilizzare. Il DbModel deve essere memorizzato nella cache e utilizzato per creare istanze di contesto. Il ModelBuilder -> flusso di lavoro DbModel è un po 'disordinato e i nomi di classe non sono grandiosi, verranno riordinati per RTM.

ho provato il seguente approccio:

  1. Spostare tutte le operazioni che erano all'interno OnModelCreating gestore di eventi per una nuova funzione che crea DbModelBuilder (è più probabile che passerà DbConnection come parametro per questa funzione)
  2. Get DbModel via DbModelBuilder.Build (DbConnecton
  3. Get DbCompiledModel tramite DbModel.Compile()
  4. Creare nuova costruzione per DbContext con i parametri (DbConnection, DbCompileModel, bool) e superare creato in precedenza DbCompiledModel suo interno

Il risultato è stato che posso cambiare i parametri di DbCompiledModel ogni volta che chiamo il costruttore DbContext. Era tutto ciò di cui avevo bisogno.

3

Inoltro avviso: è ovvio che il meccanismo mostrato di seguito coprirà le vostre esigenze purché non sia necessario eseguire join tra tabelle provenienti da contesti diversi. Se hai bisogno di tali operazioni, dovrai perfezionare ulteriormente il meccanismo mostrato di seguito con una piccola API in modo da poter associare dinamicamente dette tabelle con una stringa o un numero (in modo da poter accedere e combinare dinamicamente i rispettivi DBSet a piacimento in runtime) . Fare questo genere di cose, anche più generali, è un po 'complesso e non rientra nell'ambito di questa risposta.

Ecco una completa implementazione del meccanismo proposto da bairog: tutto il merito va a lui. Si noti che si ottiene la connessione al database tramite nuova DbContext per ragioni spiegate nei commenti:

 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; } 
     } 
    } 
+0

Sembra fantastico! Se hai un modello complesso (molte tabelle) come potresti modificare Spawn sopra? Puoi aggiungere uno snippet al tuo esempio precedente su come un chiamante dovrebbe impostare e usare FooContext, ad esempio se hai più nomi di schemi? – cardinalPilot

+0

Ho aggiornato l'implementazione per includere un esempio (nei commenti) e per mostrare anche il caso in cui è possibile mappare più tabelle. Spero che funzioni per te! – xDisruptor

+1

Nota: è molto più veloce memorizzare nella cache un DbCompiledModel anziché un DbModelBuilder. Come scritto sopra, .Build(). Compile esegue molto ed è molto costoso. Ancora un'ottima soluzione, comunque. – cardinalPilot

2

Ho lo stesso tipo di problema: contesto un db, 2 o più differenti modelli db (diverso da nomi di tabella solo)

mia soluzione per EF6: Si può ancora utilizzare il caching Entity Framework interna del modello db ma effettuare una differenziazione tra DbModel (s) sullo stesso DbContext implementando IDbModelCacheKeyProvider interfaccia sul derivato DbContext.

MSDN doc è qui: https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.idbmodelcachekeyprovider(v=vs.113).aspx E dice:

implementare questa interfaccia sul proprio contesto per usare la logica personalizzata per calcolare la chiave utilizzato per cercare un modello già creato nella cache. Questa interfaccia consente di disporre di un singolo tipo di contesto che può essere utilizzato con modelli diversi nello stesso AppDomain o più tipi di contesto che utilizzano lo stesso modello.

Spero che aiuti qualcuno.