成功创建模型后,EF Core 将永久缓存它,除非您实现的缓存管理器能够判断一个模型是否与另一个模型等效,因此它可以被缓存。
入口点是实现缓存管理器:
internal sealed class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create([NotNull] DbContext context)
{
return GetKey(context);
}
}
您必须编写的GetKey 方法必须返回一个将用作键的对象。此方法应检查提供的上下文并在模型相同时返回相同的键,而在模型不同时返回不同的键。更多关于IModelCacheKeyFactory Interface。
我明白,这可能不太清楚(我也不适合我),所以我写了一个完整的例子来说明我在生产中拥有的东西。
一个工作示例
我的目标是为不同的模式使用相同的上下文。我们需要做的是
- 创建一个新的上下文选项
- 在上下文中实现逻辑
- 创建缓存密钥工厂
- 制作扩展方法以指定架构
- 在 db 上下文中调用扩展方法
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。