如何在运行时使用Entity Framework动态添加/删除字段?

时间:2012-01-03 10:06:50

标签: c# entity-framework

我的项目中要求在运行时动态添加/删除字段。我已经创建了这个功能但不是EF。我想用EF创建这个功能。任何想法如何做到这一点?

3 个答案:

答案 0 :(得分:3)

如果您需要动态数据结构,则必须使用完全不同的体系结构,否则无法使用EF。 EF不是动态更改数据库的工具 - 只看看你如何使用EF。您将静态表映射到静态类定义。如果在运行时更改表,如何更改类(EF中没有dynamic支持)?

作为备用数据库结构的示例,您可以使用具有多个不同类型的预定义列的数据库,并将新字段映射到新类型。您将在应用程序中添加一层“映射”,它将根据存储在其他位置的某些类型元数据来解释您的数据库内容。这是例如Sharepoint(至少2007)使用的方法,其中您有单独存储的内容类型(数据描述),但所有内容都在一个包含大量列的大表中。

另一个示例是结构,其中您有核心数据的单独表(在所有实体之间共享),用于属性描述的单独表和用于与核心数据相关的属性值的单独表。这个数据库结构有一个名字,但我现在不记得了。

每种动态方法都有其代价。一般来说,你会失去一些强类型作品的部分,你会失去表现。

您的链接工具不适用于更改数据库结构。它只是用于修改映射中的名称,但表结构必须保持不变。

答案 1 :(得分:2)

这是来自Rowan Miller Blog的takne verbatin。

http://romiller.com/2012/03/26/dynamically-building-a-model-with-code-first/

仍然需要更改,以便从某些配置源或数据库表中的字段读取类的属性。

使用代码优先动态构建模型 发表于2012年3月26日。

我最近就这个主题回复了几封电子邮件,所以感觉好像是时候把它变成博客了。

在这种情况下,构成模型的类集在编译时是未知的,并且是在运行时动态发现的。这种场景的一个例子是WordPress / Orchard /等。允许插入模块的样式网站。这些模块位于单独的程序集中,可能包含表示要在应用程序数据库中保留的数据的类。这些类都被引入用于数据访问的中央Code First模型。

查找类 有许多不同的方法可用于识别要包含在模型中的类。对于此示例,我们使用[Persistent]属性,如下所示:

[AttributeUsage(AttributeTargets.Class)] 公共类PersistentAttribute:Attribute { } 现在我们可以为上下文的OnModelCreating方法添加一些逻辑来扫描程序集并添加具有[Persist]属性的任何类。我们假设可能包含类的程序集都被加载到当前的AppDomain中,当然您可能还有一些其他机制可以提供要检查的程序集列表。

public class MyContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");

    foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
      var entityTypes = assembly
        .GetTypes()
        .Where(t =>
          t.GetCustomAttributes(typeof(PersistentAttribute), inherit: true)
          .Any());

      foreach (var type in entityTypes)
      {
        entityMethod.MakeGenericMethod(type)
          .Invoke(modelBuilder, new object[] { });
      }
    }
  }
}

在此示例中,动态发现整个模型,但您也可以使用modelBuilder注册模型的某些静态部分。

使用EntityConfiguration的替代方案 Code First允许您创建一个派生自EntityTypeConfiguration的类,以封装实体的Fluent API配置。如果您使用这种配置方法,您可以只查找这些配置类并注册它们,而不是找到要注册的实体。请注意,我们正在过滤掉EntityFramework程序集,因为它有一些内部使用的配置类。

public class MyContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    var addMethod = typeof(ConfigurationRegistrar)
      .GetMethods()
      .Single(m => 
        m.Name == "Add" 
        && m.GetGenericArguments().Any(a => a.Name == "TEntityType"));

    foreach (var assembly in AppDomain.CurrentDomain
      .GetAssemblies()
      .Where(a => a.GetName().Name != "EntityFramework"))
    {
      var configTypes = assembly
        .GetTypes()
        .Where(t => t.BaseType != null
          && t.BaseType.IsGenericType
          && t.BaseType.GetGenericTypeDefinition() ==     typeof(EntityTypeConfiguration<>));

      foreach (var type in configTypes)
      {
        var entityType = type.BaseType.GetGenericArguments().Single();

        var entityConfig = assembly.CreateInstance(type.FullName);
        addMethod.MakeGenericMethod(entityType)
          .Invoke(modelBuilder.Configurations, new object[] { entityConfig     });
      }
    }
  }
}

如果型号发生变化怎么办? Code First迁移到救援......您可能无法使用从管理器管理器控制台迁移,因为发现模型的逻辑可能需要您运行完整的应用程序。幸运的是,这些命令只是API上的瘦包装器。以下是在将新类或属性添加到模型时自动更改数据库的一些代码。我最近的博客文章提供了有关从代码调用迁移的更多详细信息。

var config = new DbMigrationsConfiguration<MyContext> {     AutomaticMigrationsEnabled = true };
var migrator = new DbMigrator(config);
migrator.Update();

答案 2 :(得分:1)

在运行时添加和删除字段不是一个好主意 数据库结构应该是静态的,因为它应该反映您的类结构,并且您的类结构是静态的,无论您是否需要它。

如果你有一个动态表,你最好实现它,例如,有一个表格,其中包含字段“rowID,field,value”,并以这种方式填写值。

如果确实需要在运行时向表中添加/删除字段,可以创建添加/删除字段的存储过程,然后使用实体框架映射SP。 但是,这可能会导致很多问题,因为实体框架将无法映射/取消映射新的/删除的字段,因此在您尝试访问该表时会崩溃。