简体   繁体   English

如何在Entity Framework 6中禁用模型缓存(Code First方法)

[英]How to disable model caching in Entity Framework 6 (Code First approach)

Following MSDN documentation we can read: MSDN文档之后我们可以阅读:

The model for that context is then cached and is for all further instances of the context in the app domain. 然后缓存该上下文的模型,该模型适用于app域中上下文的所有其他实例。 This caching can be disabled by setting the ModelCaching property on the given ModelBuidler , but note that this can seriously degrade performance. 可以通过在给定的ModelBuidler上设置ModelCaching属性来禁用此缓存 ,但请注意,这会严重降低性能。

The problem is the model builder does not contain any property named ModelCaching . 问题是模型构建器不包含任何名为ModelCaching的属性。

How it is possible to disable the model caching (eg for changing model configuration in a run-time)? 如何禁用模型缓存(例如,在运行时更改模型配置)?

I have the same kind of issue: one db context, 2 or more different db models (different by table names, only) 我有同样的问题:一个数据库上下文,两个或更多不同的数据库模型(仅由表名称不同)

My solution for EF6: One can still use the internal Entity Framework caching of db model but make a differentiation between DbModel(s) on the same DbContext by implementing IDbModelCacheKeyProvider Interface on derived DbContext. 我的EF6解决方案:我们仍然可以使用db模型的内部实体框架缓存,但通过在派生的DbContext上实现IDbModelCacheKeyProvider接口 ,在同一个DbContext上区分DbModel。

MSDN doc is here: https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.idbmodelcachekeyprovider(v=vs.113).aspx And it says: MSDN doc在这里: https//msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.idbmodelcachekeyprovider(v= vs.113).aspx它说:

Implement this interface on your context to use custom logic to calculate the key used to lookup an already created model in the cache. 在您的上下文中实现此接口,以使用自定义逻辑来计算用于在缓存中查找已创建的模型的键。 This interface allows you to have a single context type that can be used with different models in the same AppDomain, or multiple context types that use the same model. 此接口允许您具有可以与同一AppDomain中的不同模型一起使用的单个上下文类型,或者使用相同模型的多个上下文类型。

Hope it helps someone. 希望它可以帮助某人。

Maybe old EF4 Docu still in EF6. 也许旧的EF4 Docu还在EF6中。 see modelBuilder.CacheForContextType not available 请参阅modelBuilder.CacheForContextType不可用

See also the DbContext Constructor to pass in a model DbContext Constructor 另请参阅DbContext构造函数以传入模型DbContext构造函数

I haven't tried it, but theoretically you can pass in a model each time. 我没试过,但理论上你每次都可以传入一个模型。

Forward Warning: It goes without saying that the mechanism shown below will cover your needs as long as you don't need to perform joins between tables that come from different contexts. 前向警告:不言而喻,只要您不需要在来自不同上下文的表之间执行连接,下面显示的机制将满足您的需求。 If you need such operations then you will have to further refine the mechanism shown below with a small API so that you can dynamically associate said tables with some string or number (so that you may access and combine their respective DBSets dynamically at will in runtime). 如果您需要这样的操作,那么您将不得不使用一个小API进一步细化下面显示的机制,以便您可以动态地将所述表与一些字符串或数字相关联(这样您就可以在运行时随意动态访问和组合各自的DBSets) 。 Doing this sort of thing -altough more general- is a bit complex and falls outside the scope of this answer. 做这种事情 - 更一般 - 有点复杂,不属于这个答案的范围。

Here's a full-blown implementation of the mechanism put forward by bairog - all credit goes to him. 这是bairog提出的机制的全面实施 - 所有信用都归他所有。 Notice that we get the connection to the database via new DbContext for reasons explained in the comments: 请注意,我们通过新的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; }
         }
     }

Here is similar question 是类似的问题

The only available approach comes from the Program Manager of the Entity Framework team ( Rowan Miller (MSFT) ): 唯一可用的方法来自实体框架团队的项目经理( Rowan Miller(MSFT) ):

We removed CacheForContextType in CTP5, we originally intended it to be used when folks wanted to use the same context in the same AppDomain with different models. 我们删除了CTP5中的CacheForContextType,我们原本打算在人们想要在同一AppDomain中使用不同模型的相同上下文时使用它。 The issue is that it would create the model on every initialization and didn't allow any way to cache a series of models and choose which one to use during each initialization. 问题是它会在每次初始化时创建模型,并且不允许任何方式缓存一系列模型并选择在每次初始化期间使用哪一个模型。 Model creation is expensive so we wanted to promote a better pattern. 模型创建很昂贵,所以我们想要推广一个更好的模式。

The pattern we recommend is to externally create a ModelBuilder -> DbDatabaseMapping -> DbModel for each model you want to use. 我们建议的模式是为您要使用的每个模型外部创建一个ModelBuilder - > DbDatabaseMapping - > DbModel。 The DbModel should be cached and used to create context instances. 应该缓存DbModel并将其用于创建上下文实例。 The ModelBuilder -> DbModel workflow is a little messy and the class names aren't great, they will be tidied up for RTM. 模型构建器 - > DbModel工作流程有点乱,类名不是很好,它们将被整理为RTM。

I've tried the following approach: 我尝试过以下方法:

  1. Move all operations which were inside OnModelCreating event handler to a new function which creates DbModelBuilder (it's more likely you will pass DbConnection as a parameter to this function) OnModelCreating事件处理程序内的所有操作移动到创建DbModelBuilder的新函数(您更有可能将DbConnection作为参数传递给此函数)
  2. Get DbModel via DbModelBuilder.Build(DbConnecton) 通过DbModelBuilder.Build(DbConnecton)获取DbModel
  3. Get DbCompiledModel via DbModel.Compile() 通过DbModel.Compile()获取DbCompiledModel
  4. Create new constructor for DbContext with parameters (DbConnection, DbCompileModel, bool) and pass previously created DbCompiledModel inside it 使用参数(DbConnection,DbCompileModel,bool)DbContext创建新的构造函数,并在其中传递先前创建的DbCompiledModel

The result was that I can change parameters of DbCompiledModel every time I call DbContext constructor. 结果是每次调用DbContext构造函数时我都可以更改DbCompiledModel的参数。 That was all I needed. 这就是我所需要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 实体框架:如何在“代码优先”迁移中禁用模型兼容性检查 - Entity Framework: How to disable model compatibility check in Code First Migrations 实体框架 - 代码优先方法 - Entity Framework - Code First Approach 实体框架代码优先方法 - Entity Framework Code First Approach 实体框架如何使用代码优先方法生成此SQL代码? - Entity Framework how to produce this SQL code with code first approach? 如何通过实体框架代码优先方法定义导航属性 - How to define a navigation property via Entity Framework code first approach 如何使用实体框架代码优先方法正确创建数据库 - How to correctly create a database using entity framework code first approach 如何使用Entity Framework 7在代码中首先处理这两个模型? - How handle these two models in code first approach using Entity Framework 7? 在实体框架代码第一种方法中映射字典 - Map a Dictionary in Entity Framework Code First Approach 外键与实体框架代码优先方法 - Foreign key with entity framework code first approach 使用Entity Framework的“代码优先”方法在模型的构造函数中分配值是否正确? - Is it correct to assign values in a model's constructor in Entity Framework's Code-First approach?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM