12

Sau MSDN documentation chúng ta có thể đọc:Làm thế nào để vô hiệu hóa mô hình bộ nhớ đệm trong Entity Framework 6 (Mã số phương pháp tiếp cận đầu tiên)

Mô hình với bối cảnh đó sau đó được lưu trữ và là dành cho tất cả các trường tiếp tục bối cảnh trong lĩnh vực ứng dụng . Bộ nhớ đệm này có thể bị tắt bằng cách đặt thuộc tính ModelCaching trên ModelBuidler nhất định, nhưng lưu ý rằng điều này có thể làm suy giảm nghiêm trọng hiệu suất.

Sự cố là trình tạo mô hình không chứa bất kỳ thuộc tính nào có tên ModelCaching.

Làm cách nào để vô hiệu hóa bộ đệm mô hình (ví dụ: để thay đổi cấu hình mô hình trong thời gian chạy)?

+0

Hy vọng bạn có thể chấp nhận câu trả lời bên dưới. – Sampath

+0

Tôi đã gặp phải sự cố tương tự - bạn có tìm thấy giải pháp nào không? – bairog

+0

Thật không may là không. Tôi đã thay đổi cách tiếp cận của mình trong dự án nên không cần phải điều tra vấn đề này nữa. – Kryszal

Trả lời

1

Here cũng tương tự như câu hỏi

Cách tiếp cận có sẵn chỉ xuất phát từ Giám đốc chương trình của nhóm Entity Framework (Rowan Miller (MSFT)):

Chúng tôi loại bỏ CacheForContextType trong CTP5, chúng tôi ban đầu dự định nó sẽ được sử dụng khi mọi người muốn sử dụng cùng một ngữ cảnh trong cùng một AppDomain với các mô hình khác nhau. Vấn đề là nó sẽ tạo ra mô hình trên mỗi lần khởi tạo và không cho phép bất kỳ cách nào để lưu vào bộ nhớ cache một loạt các mô hình và chọn cái nào để sử dụng trong mỗi lần khởi tạo. Việc tạo mô hình rất tốn kém nên chúng tôi muốn quảng bá một mô hình tốt hơn.

Mẫu mà chúng tôi đề xuất là tạo ra một ModelBuilder -> DbDatabaseMapping -> DbModel cho mỗi mô hình bạn muốn sử dụng. Các DbModel nên được lưu trữ và được sử dụng để tạo ra các bối cảnh ngữ cảnh. Dòng công việc ModelBuilder -> DbModel hơi lộn xộn và tên lớp không tuyệt vời, chúng sẽ được dọn dẹp cho RTM.

Tôi đã thử phương pháp sau đây:

  1. Move tất cả các hoạt động đó là bên OnModelCreating xử lý sự kiện cho một chức năng mới mà tạo ra DbModelBuilder (đó là nhiều khả năng bạn sẽ vượt qua DbConnection làm tham số cho hàm này)
  2. Nhận DbModel qua DbModelBuilder.Build (DbConnecton)
  3. Nhận DbCompiledModel qua DbModel.Biên dịch()
  4. Tạo constructor mới cho DbContext với các thông số (DbConnection, DbCompileModel, bool) và vượt qua trước đó tạo DbCompiledModel bên trong nó

Kết quả là tôi có thể thay đổi các thông số của DbCompiledModel mỗi khi tôi gọi DbContext hàm tạo. Đó là tất cả những gì tôi cần.

3

Chuyển tiếp Cảnh báo: Không cần phải nói rằng cơ chế được hiển thị bên dưới sẽ bao gồm nhu cầu của bạn miễn là bạn không cần thực hiện các kết nối giữa các bảng đến từ các ngữ cảnh khác nhau. Nếu bạn cần các thao tác như vậy thì bạn sẽ phải tinh chỉnh thêm cơ chế được hiển thị bên dưới với một API nhỏ để bạn có thể liên kết động các bảng đã nói với một số chuỗi hoặc số (để bạn có thể truy cập và kết hợp các DBSets tương ứng theo ý muốn) . Làm như thế này - tổng quát hơn - hơi phức tạp và nằm ngoài phạm vi của câu trả lời này.

Đây là một cơ chế thực hiện đầy đủ cơ chế được đưa ra bởi bairog - tất cả tín dụng đều được gửi đến anh ta. Chú ý rằng chúng tôi nhận được kết nối với cơ sở dữ liệu thông qua DbContext mới vì những lý do giải thích trong các ý kiến:

 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

Điều này có vẻ tuyệt vời! Nếu bạn có một mô hình phức tạp (nhiều bảng), bạn có thể sửa đổi Spawn như thế nào? Bạn có thể thêm đoạn mã vào ví dụ trên của bạn về cách người gọi sẽ thiết lập và sử dụng FooContext không, nếu bạn có nhiều tên lược đồ? – cardinalPilot

+0

Tôi đã cập nhật triển khai để bao gồm ví dụ (trong nhận xét) và cũng hiển thị trường hợp có thể ánh xạ nhiều bảng. Hi vọng nó sẽ giúp ích cho bạn! – xDisruptor

+1

Lưu ý: nó nhanh hơn nhiều để cache một DbCompiledModel thay vì một DbModelBuilder. Như đã viết ở trên, .Build() Biên dịch thực hiện rất nhiều và rất tốn kém. Vẫn là một giải pháp tuyệt vời, tho. – cardinalPilot

2

Tôi có cùng một loại vấn đề: bối cảnh một db, 2 hoặc mô hình db khác nhau hơn (khác nhau bằng tên bảng , chỉ)

giải pháp của tôi cho EF6: người ta vẫn có thể sử dụng bộ nhớ đệm Entity Framework nội bộ của mô hình db nhưng làm cho một sự khác biệt giữa DbModel (s) trên DbContext cùng bằng cách thực hiện IDbModelCacheKeyProvider Interface trên có nguồn gốc DbContext.

MSDN doc là ở đây: https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.idbmodelcachekeyprovider(v=vs.113).aspx Và nó nói:

Thực hiện giao diện này vào ngữ cảnh của bạn để sử dụng logic tùy chỉnh để tính toán chính được sử dụng để tra cứu một mô hình đã được tạo ra trong bộ nhớ cache. Giao diện này cho phép bạn có một kiểu ngữ cảnh duy nhất có thể được sử dụng với các mô hình khác nhau trong cùng một AppDomain hoặc nhiều kiểu ngữ cảnh sử dụng cùng một mô hình.

Hy vọng nó sẽ giúp ai đó.

Các vấn đề liên quan