实体框架代码 首先为映射到同一类的不同表返回相同的数据

本文关键字:一类 数据 返回 代码 框架 实体 映射 | 更新日期: 2023-09-27 18:31:13

我有一个类Timer,我想将其用于具有相同结构的不同表,因此我正在传入表名。

public class TimerContext : DbContext
{
    public DbSet<Timer> Timers { get; set; }
    private readonly string _tableName;
    public TimerContext(string tableName) : base("name=fooDb")
    {
        _tableName = tableName;
    }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Timer>().ToTable(_tableName);
        base.OnModelCreating(modelBuilder);
    }
}

但是,当我传入两个不同的表名时,它们返回相同的数据。 prevTimers包含与currTimers完全相同的数据。如何从每个表中获取唯一数据?为什么我为两个不同的表获得相同的数据?

var currTimers = new TimerContext(currentTimerTableName).Timers.ToList();
var prevTimers = new TimerContext(previousTimerTableName).Timers.ToList();

实体框架代码 首先为映射到同一类的不同表返回相同的数据

EF 将调用 OnModelCreate 方法,以在需要时立即创建模型的内存中副本。 完成此操作后,将使用此副本。 在您的情况下,生成 prevTimers 的代码使用此内存中模型,该模型映射到当前计时器表。 如果在 OnModelCreate 方法上放置断点,则应看到它只被调用一次。

综上所述,可以深入挖掘和询问内存模型(您必须使用老式上下文类型,ObjectContext vs. DbContext)。 在此处使用 Rowan Miller 的一些代码,您可以找到映射到每个实体集的表。 使用此代码,表变量中的每个项都有一个包含数据库表名称的读/写表属性。 我还没有尝试设置这个,但它似乎确实合理。 当然,您需要在 OnModelCreating 方法之外的其他位置(例如在构造函数中)更改模型,以便每次创建上下文实例时都会触发代码。

*更新*

因为我总是对学习新事物感兴趣,所以我不能不管它,而是把一个测试应用程序放在一起。 不幸的是,看起来您无法设置该属性(尽管它是一个读/写属性),因为它会抛出一个 InvalidOperationException,指出该项目是只读的。 也许还有另一种方法,但我还没有找到它...还。

*更新*

解决方案实际上比我第一次提到的要简单得多。 DbContext 类的几个构造函数接受 DbCompiledModel 类的实例作为其参数之一。 使用 DbModelBuilder 类,您可以生成通常放入 OnModelCreating 方法的相同代码。 可以调用此类的 Build 方法来创建 DbModel 类的实例。 可以调用此类的 Compile 方法来创建 DbCompiledModel 类的实例。 唯一真正的技巧是 Build 方法需要一些额外的信息(我使用了 DbProviderInfo 类的实例,但我认为您也可以使用实际连接,但这可能会导致对数据库的打击)。 我已经测试过这个,这个确实可以按预期工作。

像...

DbModelBuilder builder = null;
builder = new DbModelBuilder();
builder.Entity<TestEntity>().ToTable(tableName);
DbModel model1 = null;
model1 = builder.Build(new DbProviderInfo("System.Data.SqlClient", "2012"));
builder.Entity<TestEntity>().ToTable(anotherTableName);
DbModel model2 = null;
model2 = builder.Build(new DbProviderInfo("System.Data.SqlClient", "2012"));
DbCompiledModel compiledModel1 = null;
DbCompiledModel compiledModel2 = null;
compiledMdoel1 = model1.Compile();
compiledMdoel2 = model2.Compile();
TestContext context1 = null;
TestContext context2 = null;
context1 = new TestContext(compiledModel1);
context2 = new TestContext(compiledModel2);

当然,TestContext 类的构造函数必须将编译的模型实例传递给基构造函数。

无法添加评论,但我只想添加发生在我身上的一件事

我的代码是这样的:

DbModelBuilder builder = new DbModelBuilder();
this.OnModelCreating(builder);
var model = builder.Build(this.Database.Connection);

我以为当我将当前的 DbConnection 对象传递给此方法时,它会以某种方式"继承"所有连接设置,但似乎我错了。调试一段时间后,我才意识到它为我生成了一些奇怪的连接字符串,这总是导致找不到数据库问题。所以我的解决方案是,当实例化"TestContext"(如Jason Richmeier的答案)时,将nameOrConnectionString作为第一个参数,将编译的模型作为第二个参数,它解决了我的问题。

我想知道,由于EF保留了某个模型的内存副本,因此手动创建另一个副本只会在内存中创建新副本吗?如果我的代码需要这样做很多次,它会继续在内存中创建新模型,最后以内存溢出结束?