【问题标题】:Entity Framework Code First returning same data for different tables mapped to same class实体框架代码首先为映射到同一类的不同表返回相同的数据
【发布时间】:2015-03-23 12:40:54
【问题描述】:

我有一个类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();

【问题讨论】:

    标签: c#


    【解决方案1】:

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

    话虽如此,可以深入挖掘并询问内存模型(您必须使用 old school 上下文类型,ObjectContext 与 DbContext)。使用来自 Rowan Miller here 的一些代码,您可以找到映射到每个实体集的表。使用此代码,tables 变量中的每个项目都有一个包含数据库表名称的读/写 Table 属性。我没有尝试过设置这个,但它看起来确实是合理的。当然,您需要在 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 类的构造函数必须将编译后的模型实例传递给基构造函数。

    【讨论】:

      【解决方案2】:

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

      我的代码是这样的:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-28
        • 1970-01-01
        • 2011-08-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多