12

Po MSDN documentation możemy przeczytać:Jak wyłączyć modelu buforowanie w Entity Framework 6 (kod pierwsze podejście)

Wzór tym kontekście jest następnie buforowane i jest dla wszystkich kolejnych instancji kontekstu w domenie aplikacji . To buforowanie można wyłączyć, ustawiając właściwość ModelCaching na danym modelu ModelBuidler, ale należy pamiętać, że może to poważnie pogorszyć wydajność.

Problem polega na tym, że program budujący model nie zawiera żadnej właściwości o nazwie ModelCaching.

Jak można wyłączyć buforowanie modelu (np. W celu zmiany konfiguracji modelu w czasie wykonywania)?

+0

Mam nadzieję, że akceptujesz odpowiedź poniżej. – Sampath

+0

Mam do czynienia z tym samym problemem - czy znalazłeś jakieś rozwiązanie? – bairog

+0

Niestety nie. Zmieniłem swoje podejście w projekcie, więc nie było potrzeby bardziej wnikliwego zbadania tego problemu. – Kryszal

Odpowiedz

1

Here jest podobna kwestia

Jedyny dostępny podejście pochodzi z programu Menedżer zespołu Entity Framework (Rowan Miller (MSFT)):

Usunęliśmy CacheForContextType w CTP5, początkowo chcieliśmy, aby był używany, gdy ludzie chcieli używać tego samego kontekstu w tym samym AppDomain z różnymi modelami. Problem polega na tym, że tworzył model przy każdej inicjalizacji i nie pozwalał w żaden sposób buforować serii modeli i wybierać, który z nich będzie używany podczas każdej inicjalizacji. Tworzenie modeli jest drogie, więc chcieliśmy promować lepszy wzór.

Zalecany wzór to zewnętrznie utworzyć ModelBuilder -> DbDatabaseMapping -> DbModel dla każdego modelu, którego chcesz użyć. Model DbModel powinien być buforowany i używany do tworzenia instancji kontekstowych. Przepływ pracy ModelBuilder -> DbModel jest trochę nieporządny, a nazwy klas nie są świetne, zostaną uporządkowane dla RTM.

Próbowałem następujące podejście:

  1. przenieść wszystkie operacje, które były wewnątrz OnModelCreating obsługi zdarzeń do nowej funkcji, która tworzy DbModelBuilder (to bardziej prawdopodobne będzie zdać DbConnection jako parametr tej funkcji)
  2. Uzyskaj DbModel przez DbModelBuilder.Build (DbConnecton)
  3. Uzyskaj DbCompiledModel przez DbModel.Kompilacji()
  4. Utwórz nowe konstruktora dla DbContext z parametrami (DbConnection, DbCompileModel, bool) i przekazać wcześniej utworzony DbCompiledModel w środku

Rezultatem było to, że mogę zmienić parametry DbCompiledModel za każdym razem, gdy wywoływam konstruktor DbContext. To było wszystko, czego potrzebowałem.

3

Ostrzeżenie: To oczywiste, że przedstawiony poniżej mechanizm obejmie twoje potrzeby, o ile nie będziesz musiał wykonywać połączeń między tabelami pochodzącymi z różnych kontekstów. Jeśli potrzebujesz takich operacji, będziesz musiał udoskonalić poniższy mechanizm za pomocą małego API, aby móc dynamicznie powiązać te tabele z pewnymi ciągami znaków lub liczbami (abyś mógł w dowolnym momencie uzyskać dostęp i połączyć swoje odpowiednie DBSety dowolnie) . Robienie tego rodzaju rzeczy - choć bardziej ogólne - jest nieco skomplikowane i wykracza poza zakres tej odpowiedzi.

Oto pełnowymiarowa implementacja mechanizmu zaproponowanego przez Bairog - wszystkie zasługują na niego. Zauważmy, że możemy uzyskać połączenie z bazą danych za pomocą nowego DbContext dla powodów wyjaśnionych w komentarzach:

 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

To wygląda świetnie! Jeśli masz złożony model (wiele tabel), jak możesz zmodyfikować Spawn powyżej? Czy możesz dodać fragment kodu do powyższego przykładu na temat tego, w jaki sposób osoba wywołująca skonfigurowała i używa FooContext, powiedz jeśli masz wiele nazw schematów? – cardinalPilot

+0

Zaktualizowałem implementację, dodając przykład (w komentarzach) i pokazując, jak wiele tabel można odwzorować. Mam nadzieję, że to działa dla Ciebie! – xDisruptor

+1

Uwaga: znacznie szybsze jest buforowanie DbCompiledModel zamiast DbModelBuilder. Jak napisano powyżej, .Build(). Kompilacja wykonuje dużo i jest bardzo droga. Wciąż świetne rozwiązanie, tho. – cardinalPilot

2

Mam ten sam rodzaj problemu: kontekst jeden db, 2 lub więcej różnych modeli dB (różnej od nazwy tabel tylko)

Moje rozwiązanie dla EF6: można nadal używać wewnętrznego buforowania Entity Framework modelu db jednak dokonać rozróżnienia między DbModel (ów) w tej samej DbContext wdrażając IDbModelCacheKeyProvider interfejsu na DbContext pochodzi.

MSDN doc jest tutaj: https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.idbmodelcachekeyprovider(v=vs.113).aspx i mówi:

Wdrożenie tego interfejsu na swojej kontekście użyć niestandardowego logiki obliczyć klucz używany do wyszukiwania jest już utworzony model w pamięci podręcznej. Ten interfejs umożliwia utworzenie pojedynczego typu kontekstu, który może być używany z różnymi modelami w tym samym AppDomain lub z wieloma typami kontekstów, które używają tego samego modelu.

Mam nadzieję, że komuś pomaga.