我是MVC和EF的新手。我的应用程序是一个简单的代码优先,有几个POCO类和一个像这样的DBContext:
public class ExpDefContext : DbContext
{
public DbSet<Experiment> Experiments { get; set; }
public DbSet<Research> Researches { get; set; }
...
问题:我需要在我的数据模型中添加一个实体集,它的类型是在运行时从用户输入构建的,这意味着我不知道它的数据结构。
我读了非泛型Dbset类就是为了这个,所以我添加到上下文中:
public DbSet Log { get; set; }
...并为接受运行时类型并设置新Dbset的上下文创建了一个构造函数:
public ExpDefContext(Type LogRecType)
{
Log = Set(LogRecType);
}
(顺便说一下,使用Reflection.Emit构建类型。)
在控制器中,我创建了类型(名为LogRec)并将其传递给新的DBContext实例。然后我创建一个LogRec实例并尝试将其添加到数据库中:
Type LogRec;
LogRec = LogTypeBuilder.Build(dbExpDef, _experimentID);
var dbLog = new ExpDefContext(LogRec);
var testRec = LogRec.GetConstructor(Type.EmptyTypes).Invoke(Type.EmptyTypes);
dbLog.Log.Add(testRec);
dbLog.SaveChanges();
我从dbLog.Log.Add(testRec)得到一个异常:
实体类型LogRec不是当前上下文模型的一部分
我做错了什么? 有没有更好的方法来做到这一点(最好不要深入到实体框架中)?
谢谢
答案 0 :(得分:4)
我怀疑EF只反映了派生DbSet<T>
中的通用DbContext
属性,并在内存中创建模型时忽略了任何非泛型DbSet
属性。
但是,另一种方法可能是使用OnModelCreating
中的Fluent API将您的动态类型作为实体添加到模型中。
首先,只有在第一次加载AppDomain时在内存中构建模型时,才能向模型添加类型。 (每个AppDomain只构建一个模型。)如果除了重载的构造函数之外还有上下文的默认构造函数,并且使用此默认构造函数创建并使用了上下文实例,那么您的模型将仅使用静态类型和只要AppDomain存在,就不能再使用动态类型作为实体。这将导致您的例外。
要考虑的另一点是创建数据库模式。如果您的类型在编译时未知,则数据库模式在编译时是未知的。如果模型在下次运行应用程序时由于新类型而发生更改,则需要以某种方式更新数据库模式,方法是从头开始重新创建数据库,或者定义仅删除LogRec
表的自定义数据库初始化程序并根据LogRec
类型的新布局创建一个新表。或者Code-First Migrations可能会有所帮助。
关于使用Fluent API的可能解决方案:
删除DbSet
并将Type
成员添加到上下文并覆盖OnModelCreating
:
public class ExpDefContext : DbContext
{
private readonly Type _logRecType;
public ExpDefContext(Type LogRecType)
{
_logRecType = LogRecType;
}
public DbSet<Experiment> Experiments { get; set; }
public DbSet<Research> Researches { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
entityMethod.MakeGenericMethod(_logRecType)
.Invoke(modelBuilder, new object[] { });
}
}
DbModelBuilder
没有非通用Entity
方法,因此必须动态调用通用Entity<T>
方法。
OnModelCreating
中的上述代码是......
modelBuilder.Entity<LogRec>();
...将与静态LogRec
类型一起使用,并且只将该类型作为EF已知的实体。这与向上下文类添加DbSet<LogRec>
属性完全相同。
您应该能够使用...
访问动态实体的实体集context.Set(LogRecType)
...将返回非通用DbSet
。
我不知道这是否有效并且没有对the idea is from Rowan Miller, member of the EF team进行测试,所以我有一些希望。