代码之家  ›  专栏  ›  技术社区  ›  pckben

实体框架在运行时将模型类映射到表

  •  0
  • pckben  · 技术社区  · 7 年前

    MyTableClass )到一个未知的表名。这个表名是由用户在运行时给出的,所以这是在 OnModelCreating 上下文类的方法。有没有办法做如下伪代码:

    void OnUserEnteredTableNameFromUI(string tableName)
    {
        var modelBuilder = new ModelBuilder(???);  // how?
        modelBuilder.Entity<MyTableClass>().ToTable(tableName);
        // how to get a ref to DbSet<MyTableClass> myTable from here?
    }
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   Gert Arnold    7 年前

    我见过这样的情况:结构相同但表名不同的数据库被部署到多个站点。在这种情况下,EF只需要在应用程序启动时知道表名。

    这可以通过向上下文中添加构造函数参数来完成:

    private readonly string _userDefinedTableName;
    
    public MyContext(string userDefinedTableName)
    {
        _userDefinedTableName = userDefinedTableName;
    }
    

    然后,在 OnModelCreating :

    modelBuilder.Entity<MyTableClass>().ToTable(_userDefinedTableName);
    

    但是,在您的情况下,名称必须在运行时更改任意次数。对于实体框架,这是不可能的(好吧,更确切地说,太不切实际了,无法真正考虑它)。EF为每个上下文类编译和存储一次模型,因为对于每个上下文实例化,这样做代价太高。

    也就是说 模型创建

    您必须找到其他方法动态处理表数据,或者更改设计,以便将多个表转换为一个固定表。

        2
  •  0
  •   Ivan Stoev    7 年前

    由于这是一个有趣的问题,可能有助于其他需要一些动态模型构建的人,下面介绍如何实现它。

    public class CustomDbContext : DbContext
    {
        // …
    
        private string customTableName;
        public string CustomTableName => customTableName ?? "DefaultCustomTableName";
    }
    

    我们在里面用它 OnModelCreating

    modelBuilder.Entity<CustomEntity>().ToTable(CustomTableName); 
    

    唯一的问题是默认情况下 模型创建 每个上下文类型只调用一次并缓存。幸运的是,EF核心构建在(可替换的)服务架构之上。负责模型缓存的服务接口是 IModelCacheKeyFactory

    创建唯一标识给定上下文的模型的键。它用于存储和查找给定上下文的缓存模型。

    object Create(DbContext context)
    

    返回的对象 GetHashCode / Equals 方法用于标识传递的上下文实例。默认的EF核心服务实现返回一个比较上下文类型的对象。

    CustomTableName 在我们的案例中)。实现可以是这样的(使用Cé7值元组):

    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 覆盖:

    optionsBuilder.ReplaceService<IModelCacheKeyFactory, CustomModelCacheKeyFactory>();
    

    仅此而已。任何时候你用不同的 ,EF Core将创建一个新模型并映射 CustomEntity 去那张桌子。

    通过将所有自定义状态包含在 CustomModelCacheKey.key 元组。当然,它可以在没有值元组的情况下实现,只需使用 等于 CustomModelCacheKey 自定义服务可以直接返回包含上下文类型和自定义状态成员值的值元组。