17

按照MSDN 文档,我们可以阅读:

然后缓存该上下文的模型,并用于应用程序域中上下文的所有进一步实例。可以通过在给定的 ModelBuidler 上设置 ModelCaching 属性来禁用此缓存,但请注意,这会严重降低性能。

问题是模型构建器不包含任何名为ModelCaching的属性。

如何禁用模型缓存(例如,在运行时更改模型配置)?

4

4 回答 4

8

我有同样的问题:一个数据库上下文,2个或更多不同的数据库模型(仅表名不同)

我的 EF6 解决方案:仍然可以使用 db 模型的内部实体框架缓存,但通过在派生的 DbContext 上实现IDbModelCacheKeyProvider 接口来区分同一 DbContext 上的DbModel。

MSDN 文档在这里:https://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.idbmodelcachekeyprovider(v=vs.113).aspx 它说:

在您的上下文中实现此接口以使用自定义逻辑来计算用于在缓存中查找已创建模型的键。此接口允许您拥有可与同一 AppDomain 中的不同模型一起使用的单个上下文类型,或使用相同模型的多个上下文类型。

希望它可以帮助某人。

于 2017-10-31T23:34:58.480 回答
4

也许旧的 EF4 Docu 仍在 EF6 中。请参阅 modelBuilder.CacheForContextType 不可用

另请参阅 DbContext 构造函数以传入模型DbContext 构造函数

我没试过,但理论上你每次都可以传入一个模型。

于 2015-04-20T20:54:34.330 回答
4

前向警告:不用说,只要您不需要在来自不同上下文的表之间执行连接,下面显示的机制就可以满足您的需求。如果您需要这样的操作,那么您将不得不使用一个小的 API 进一步完善下面显示的机制,以便您可以将所述表与一些字符串或数字动态关联(以便您可以在运行时随意访问和组合它们各自的 DBSet) . 做这种事情 - 虽然更普遍 - 有点复杂,超出了这个答案的范围。

这是 bairog 提出的机制的全面实施——所有功劳都归功于他。请注意,由于注释中解释的原因,我们通过 new 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; }
         }
     }
于 2017-01-31T20:20:12.263 回答
1

是类似的问题

唯一可用的方法来自实体框架团队的项目经理 ( Rowan Miller (MSFT) ):

我们在 CTP5 中删除了 CacheForContextType,我们最初的目的是在人们想要在同一个 AppDomain 中使用不同模型的相同上下文时使用它。问题是它会在每次初始化时创建模型,并且不允许以任何方式缓存一系列模型并在每次初始化期间选择要使用的模型。模型创建很昂贵,所以我们想推广更好的模式。

我们推荐的模式是为您要使用的每个模型在外部创建一个 ModelBuilder -> DbDatabaseMapping -> DbModel。DbModel 应该被缓存并用于创建上下文实例。ModelBuilder -> DbModel 工作流程有点混乱,类名也不是很好,它们将被整理以用于 RTM。

我尝试了以下方法:

  1. OnModelCreating事件处理程序中的所有操作移至创建DbModelBuilder的新函数(您更有可能将DbConnection作为参数传递给此函数)
  2. 通过DbModelBuilder.Build(DbConnecton)获取DbModel
  3. 通过DbModel.Compile()获取DbCompiledModel
  4. 使用参数(DbConnection、DbCompileModel、bool)为DbContext创建新的构造函数,并在其中传递先前创建的 DbCompiledModel

结果是我每次调用DbContext构造函数时都可以更改DbCompiledModel的参数。这就是我所需要的。

于 2016-11-23T04:43:09.670 回答