实体框架核心-禁用模型缓存,为每个实例dcontext

如何解决实体框架核心-禁用模型缓存,为每个实例dcontext

文档说:该上下文的模型已缓存,并且适用于应用程序域中该上下文的所有其他实例。可以通过在给定的ModelBuidler上设置ModelCaching属性来禁用此缓存

但是我找不到办法。我必须禁用缓存,因为我要在运行时添加模型,并从程序集和创建数据库中加载所有模型。

我发现此链接表示实现此目的的一种方法是使用DBModelBuilding-将模型手动添加到上下文中,但这是针对Entity Framework,而对EF Core则无济于事。

Entity Framework 6. Disable ModelCaching

我希望有人对此有解决方案。

谢谢

解决方法

您需要change the cache key来正确表示您正在构建的模型/使其与众不同。

  1. 在派生的 DbContext 上实现 IDbModelCacheKeyProvider 接口。看一下这个 https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.infrastructure.idbmodelcachekeyprovider?redirectedfrom=MSDN&view=entity-framework-6.2.0

  2. Build the model outside DbContext 然后是 provide it in the options

,

成功创建模型后,EF Core 将对其进行永久缓存,除非您实现了一个缓存管理器,该管理器能够判断一个模型是否与另一个模型等效,从而可以对其进行缓存。

入口点是实现缓存管理器:

internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
    public object Create([NotNull] DbContext context)
    {
        return GetKey(context);
    }
}

您必须编写的 GetKey 方法必须返回一个将用作键的对象。这个方法应该检查提供的上下文并在模型相同时返回相同的键,当模型不同时返回不同的键。有关 IModelCacheKeyFactory Interface 的更多信息。

我明白,这可能不清楚(我也不知道),所以我写了一个完整的例子来说明我在生产中的情况。

一个工作示例

我的目标是为不同的模式使用相同的上下文。我们需要做的是

  1. 创建一个新的上下文选项
  2. 在上下文中实现逻辑
  3. 创建缓存密钥工厂
  4. 使扩展方法指定架构
  5. 在数据库上下文中调用扩展方法

1.创建一个新的上下文选项

这里有一个仅包含 _schemaName 的样板。样板文件是必要的,因为扩展选项在设计上是不可变的,我们需要保留合同。

internal class MySchemaOptionsExtension : IDbContextOptionsExtension
{
    private DbContextOptionsExtensionInfo? _info;
    private string _schemaName = string.Empty;

    public MySchemaOptionsExtension()
    {
    }

    protected MySchemaOptionsExtension(MySchemaOptionsExtension copyFrom)
    {
        _schemaName = copyFrom._schemaName;
    }

    public virtual DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this);

    public virtual string SchemaName => _schemaName;

    public virtual void ApplyServices(IServiceCollection services)
    {
        // not used
    }

    public virtual void Validate(IDbContextOptions options)
    {
        // always ok
    }

    public virtual MySchemaOptionsExtension WithSchemaName(string schemaName)
    {
        var clone = Clone();

        clone._schemaName = schemaName;

        return clone;
    }

    protected virtual MySchemaOptionsExtension Clone() => new(this);

    private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
    {
        private const long ExtensionHashCode = 741; // this value has chosen has nobody else is using it

        private string? _logFragment;

        public ExtensionInfo(IDbContextOptionsExtension extension) : base(extension)
        {
        }

        private new MySchemaOptionsExtension Extension => (MySchemaOptionsExtension)base.Extension;

        public override bool IsDatabaseProvider => false;

        public override string LogFragment => _logFragment ??= $"using schema {Extension.SchemaName}";

        public override long GetServiceProviderHashCode() => ExtensionHashCode;

        public override void PopulateDebugInfo([NotNull] IDictionary<string,string> debugInfo)
        {
            debugInfo["MySchema:" + nameof(DbContextOptionsBuilderExtensions.UseMySchema)] = (ExtensionHashCode).ToString(CultureInfo.InvariantCulture);
        }
    }
}

2.上下文中的逻辑

在这里,我们将模式强制为所有真实实体。模式由附加到上下文的选项获得

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   var options = this.GetService<IDbContextOptions>().FindExtension<MySchemaOptionsExtension>();
   if (options == null)
   {
       // nothing to apply,this is a supported scenario.
       return;
   }

   var schema = options.SchemaName;

   foreach (var item in modelBuilder.Model.GetEntityTypes())
   {
       if (item.ClrType != null)
           item.SetSchema(schema);
   }
}

3.创建缓存密钥工厂

这里我们需要创建缓存工厂,它会告诉 EF Core 它可以在同一上下文中缓存所有模型,即所有具有相同架构的上下文将使用相同的模型:

internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
    public object Create([NotNull] DbContext context)
    {
        const string defaultSchema = "dbo";
        var extension = context.GetService<IDbContextOptions>().FindExtension<MySchemaOptionsExtension>();

        string schema;
        if (extension == null)
            schema = defaultSchema;
        else
            schema = extension.SchemaName;

        if (string.IsNullOrWhiteSpace(schema))
            schema = defaultSchema;
        // ** this is the magic **
        return (context.GetType(),schema.ToUpperInvariant());
    }
}

魔法就在这一行

return (context.GetType(),schema.ToUpperInvariant());

我们返回一个包含上下文类型和模式的元组。元组的散列组合了每个条目的散列,因此类型和模式名称是这里的逻辑鉴别器。当它们匹配时,模型被重用;如果没有,则会创建一个新模型,然后缓存。

4.制作扩展方法

扩展方法只是隐藏了选项的添加和缓存服务的替换。

public static DbContextOptionsBuilder UseMySchema(this DbContextOptionsBuilder optionsBuilder,string schemaName)
{
    if (optionsBuilder == null)
        throw new ArgumentNullException(nameof(optionsBuilder));
    if (string.IsNullOrEmpty(schemaName))
        throw new ArgumentNullException(nameof(schemaName));

    var extension = optionsBuilder.Options.FindExtension<MySchemaOptionsExtension>() ?? new MySchemaOptionsExtension();

    extension = extension.WithSchemaName(schemaName);

    ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);

    optionsBuilder.ReplaceService<IModelCacheKeyFactory,MyModelCacheKeyFactory>();

    return optionsBuilder;
}

特别是,以下行适用于我们的缓存管理器:

optionsBuilder.ReplaceService<IModelCacheKeyFactory,MyModelCacheKeyFactory>();

5.调用扩展方法

您可以手动创建上下文,如下所示:

var options = new DbContextOptionsBuilder<DataContext>();

options.UseMySchema("schema1")
options.UseSqlServer("connection string omitted");

var context = new DataContext(options.Options)

或者,您可以将 IDbContextFactory 与依赖项注入一起使用。有关 IDbContextFactory Interface 的更多信息。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?