由于这是一个有趣的问题,可能会帮助需要一些动态模型构建的其他人,这里是如何实现它。
假设我们有一个自定义上下文,其中包含通过构造函数提供的自定义表名(正如 Gert Arnold 在另一个答案中所建议的那样):
public class CustomDbContext : DbContext
{
// …
private string customTableName;
public string CustomTableName => customTableName ?? "DefaultCustomTableName";
}
我们在OnModelCreating 中使用它(它应该在那里,目前没有其他简单的方法可以使用预定义的约定集创建模型):
modelBuilder.Entity<CustomEntity>().ToTable(CustomTableName);
唯一的问题是默认情况下OnModelCreating 每个上下文类型只调用一次并被缓存。幸运的是,EF Core 构建在(可替换的)服务架构之上。负责模型缓存的服务接口是IModelCacheKeyFactory:
创建唯一标识给定上下文的模型的键。这用于存储和查找给定上下文的缓存模型。
只有一个方法
object Create(DbContext context)
返回的对象GetHashCode/Equals方法用于标识传递的上下文实例。默认的 EF Core 服务实现返回一个比较上下文类型的对象。
为了使自定义上下文模型正常工作,我们需要将其替换为自定义服务,该服务还可以比较自定义状态(在我们的示例中为CustomTableName)。实现可能是这样的(使用 C#7.0 值元组):
class CustomModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create(DbContext context) => new CustomModelCacheKey(context);
}
class CustomModelCacheKey
{
(Type ContextType, string CustomTableName) key;
public CustomModelCacheKey(DbContext context)
{
key.ContextType = context.GetType();
key.CustomTableName = (context as CustomDbContext)?.CustomTableName;
}
public override int GetHashCode() => key.GetHashCode();
public override bool Equals(object obj) => obj is CustomModelCacheKey other && key.Equals(other.key);
}
唯一剩下的就是用自定义替换现有服务。可以在OnConfiguring override 里面完成:
optionsBuilder.ReplaceService<IModelCacheKeyFactory, CustomModelCacheKeyFactory>();
仅此而已。每当您使用不同的 CustomTableName 创建上下文时,EF Core 都会创建一个新模型并将 CustomEntity 映射到该表。
通过在CustomModelCacheKey.key 元组中包含所有自定义状态,可以将相同的技术应用于包含影响状态的自定义模型的任何上下文。当然,它可以在没有值元组的情况下实现,只是使用它们GetHashCode 和Equals 覆盖更容易实现。实际上,自定义服务可以直接返回包含上下文类型和自定义状态成员值的值元组,而不是 CustomModelCacheKey。