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

在MSDN文档之后我们可以阅读:

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

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

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

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

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

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

前向警告:不言而喻,只要您不需要在来自不同上下文的表之间执行连接,下面显示的机制将满足您的需求。 如果您需要这样的操作,那么您将不得不使用一个小API进一步优化下面显示的机制,以便您可以动态地将所述表与一些字符串或数字相关联(以便您可以在运行时随意动态访问和组合各自的DBSets) 。 做这种事情 – 更一般 – 有点复杂,不属于这个答案的范围。

这是bairog提出的机制的全面实施 – 所有的功劳归于他。 请注意,我们通过新的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 { get; set; } public DbSet 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, DbCompiledModel> DbModelBuilderCache = new ConcurrentDictionary, 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(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 { public PingFluentConfiguration(string schemaName, string tableName) { HasKey(t => t.Id); ToTable(schemaName: schemaName, tableName: tableName); } } public sealed class PongFluentConfiguration : EntityTypeConfiguration { 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; } } } 

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

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

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

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

希望它可以帮到某人。

是类似的问题

唯一可用的方法来自entity framework团队的项目经理( Rowan Miller(MSFT) ):

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

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

我尝试过以下方法:

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

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