【问题标题】:Updating Database via Anonymous Type?通过匿名类型更新数据库?
【发布时间】:2011-04-14 17:17:27
【问题描述】:

以下代码从我的活动表中获取所有尚未发布到 Twitter 上的行。然后它循环并发布每一行的 Twitter 更新。在此过程中,我想更新数据库以指示这些行现在已被“推特”。

但是,当我尝试更新此值时,我遇到了错误(如下所示)。我认为这是因为我使用的是匿名类型。但是,如果我使用完整类型,则需要从数据库中提取大量不必要的数据。

有没有办法有效地做到这一点?或者这是 EF 强迫我在性能上做出妥协的又一个案例?

using (MyEntities context = new MyEntities())
{
    var activities = from act in context.Activities
                     where act.ActTwittered == false
                     select new { act.ActID, act.ActTitle, act.Category,
                     act.ActDateTime, act.Location, act.ActTwittered };

    foreach (var activity in activities)
    {
        twitter.PostUpdate("...");
        activity.ActTwittered = true; // <== Error: ActTwittered is read-only
    }
}

【问题讨论】:

  • 当然不能通过匿名类型更新数据库。匿名类型不知道数据库,它只是属性的集合。活动表有多少列?真的会有这么多数据吗?
  • 这将比现有代码返回的数据多几倍。这可能不是世界末日,但它确实加起来了。我已经厌倦了 EF 要求我编写低效代码的方式(如果你说这是必要的)。
  • @Jonathan Wood - 你不知道你在做什么吗?我们没有说服英孚,因为我们觉得还有很大的改进空间。
  • @JonH:嗯,按照我的想法,要完全理解我正在做的事情的唯一方法就是尝试一下,我一直在这样做。 EF现在风靡一时,如果我学习它,似乎会有更多的咨询机会。但到目前为止,我不喜欢我所看到的。很高兴听到我不是唯一一个。
  • @Jonathan Wood - SAP 也有很多机会。这不是因为它是多么伟大的产品。这完全是由于产品的架构非常糟糕,因此存在“机会”。

标签: c# entity-framework entity-framework-4


【解决方案1】:

您可以尝试这样的“假对象方法”:

using (MyEntities context = new MyEntities())
{
    var activities = from act in context.Activities
                     where act.ActTwittered == false
                     select new { act.ActID, act.ActTitle, act.Category,
                     act.ActDateTime, act.Location, act.ActTwittered };

    foreach (var activity in activities)
    {
        twitter.PostUpdate("...");

        // Create fake object with necessary primary key
        var act = new Activity()
        {
            ActID = activity.ActID,
            ActTwittered = false
        };

        // Attach to context -> act is in state "Unchanged"
        // but change-tracked now
        context.Activities.Attach(act);

        // Change a property -> act is in state "Modified" now
        act.ActTwittered = true;
    }

    // all act are sent to server with sql-update statements
    // only for the ActTwittered column
    context.SaveChanges();
}

这是“理论”代码,不确定它是否有效。

编辑

不再是“理论上的”。我已经用 EF 4.1 的DbContext 对此进行了测试,它的工作原理如上面示例代码中所述。 (因为DbContext 只是ObjectContext 周围的包装API,所以几乎可以肯定地假设它也适用于EF 4.0。)

【讨论】:

  • 感谢您提出解决方法。我不太了解 EF,但是我没有初始化的行中的其他列会发生什么。这不会用空值覆盖其他列吗?
  • 不,诀窍在于通过将此假对象附加到上下文 EF 假定它表示数据库中对象的当前状态(这当然是错误的)。如果我们现在更改单个属性附加后,EF的更改跟踪机制会在内部存储此只有该单个属性已更改的信息,以便稍后在我们创建sql更新命令时发出 SaveChanges。因此,数据库中的其他列不受更新的影响。如前所述,我没有测试过这样的东西。但我有一种很好的感觉,这种方法或一些非常相似的方法应该可行。
  • @Slauma:我明白了。所以你认为 EF 足够聪明,只更新标记为需要更新的列。我不知道我会使用它,但如果它有效,我同意解决所提出的问题。谢谢。
  • @Jonathan Wood:一个重要的问题:您是否将EF4.0 与ObjectContext 一起使用?如果是,您是使用派生自 EntityObject 的实体还是使用 POCO?或者您是否将 EF4.1 与 DbContext 一起使用?我的代码可能只适用于源自EntityObject 的实体。我会看看在其他情况下该怎么做。
  • @Slauma:我还没有安装 EF4.1。至于第一个问题,我是这么认为的。从我的代码中这不是很明显吗?
【解决方案2】:

如果您只是选择“行动”,那么它应该可以工作。修改后别忘了提交。

【讨论】:

  • 正如所指出的,这使我的代码从数据库中提取了更多信息,这需要更多时间。这是 EF 要求我编写效率较低的代码的又一个例子吗?
  • 正如 Akash 所说,通过创建匿名类型,您将失去与表的连接;因此,无法更新该行。只有拉出完整对象才能更新对象,这确实是您可以预期的唯一合理情况。
  • @Nik:这可能不是花时间,但我会反对最后一部分。人们可能合理地期望 EF 提供某种机制来有效地更新选定行中的单个列,而无需下载这些行的所有数据。如果没有 EF,我可以(并且将会)很容易地做到这一点。我认为没有什么可以阻止 EF 采用此类技术。但是话又说回来,我已经编程很多年了,对这些事情很有意见。 :-)
  • 提供一种机制来更新单个列,当然,我会喜欢它,我认为它应该在那里。但是,当您查看您拥有的代码时,您会要求 EF 知道每列来自哪个表,然后在该行之上,这可能需要它找出要收集的额外 ID 列。我认为开销可能比仅仅获得整行更大。
  • @Nik:同样,这可能不是花时间,但解决它的许多方法之一是提供一种机制,让我使用完整的实体,但有一种方法可以指定我只想检索一些列。事实上,EF 可能会在某个时候很好地实现类似的东西。是的,显然,它不能用我拥有的代码来做到这一点。但我们不要暗示这是无法完成的事情。
【解决方案3】:

为什么要调用 select new 而不是返回整个对象。实体框架只有在模式资源中正确定义属性时才能更新属性,而匿名类型肯定不是这种情况。

实体框架永远无法确定属性映射到哪个表和哪个字段。

【讨论】:

  • 正如我在问题中解释的那样,我不会返回整个对象,因为我想提高效率。看起来高效与 EF 不兼容?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-19
相关资源
最近更新 更多