如何将记录对象正确附加到Entity Framework中的数据上下文?

时间:2011-10-05 07:33:31

标签: c# entity-framework

我需要为某些记录切换数据上下文。所以基本上我有db上下文A和B,我使用A获取记录,然后切换到B,更改记录,并保存它们。

当我为B调用Attach时,我得到了多个数据上下文使用记录的异常,当我为A添加Detach时,我得到异常,那些记录没有附加到A.

那么如何切换数据上下文呢?

实施例

db_creator 是db context的创建者。在这里,我获取数据(更正版本):

using (var db = db_creator.Create())
{
  var coll = db.Mailing.Where(it => !it.mail_IsSent).ToList(); // (*)
  coll.ForEach(it => db.Detach(it));
  return coll;
}

(*)错误是由重构这一部分造成的,我创建了额外的数据上下文,然后我试图从另一个中分离记录。

现在我想将数据上下文切换到新的,进行一些计算和修改并保存记录。 coll 是记录的列表

using (var db = db_creator.Create())
{
  coll.ForEach(it => db.Mailing.Attach(it));
  ...
  db.SaveChanges();
}

2 个答案:

答案 0 :(得分:1)

我建议您更改设计,一次只能有一个上下文。 (根据您的项目类型,这可能会有所不同。通常在网络应用中,每个http请求都有一个上下文。)

例如,在Web应用程序中,您可以执行以下操作:

protected MyContext Context
{
    get
    {
        var context = HttpContext.Current.Items["MyContext"];
        if (context == null)
        {
            context = new MyContext();
            HttpContext.Current.Items.Add("MyContext", context);
        }
        return context as MyContext;
    }
}  

并将其丢弃在Application_EndRequest中:

app.EndRequest += (sender, args) =>
{
    HttpContext.Current.Items.Remove("MyContext");
}

如果您有多种项目类型,请考虑使用Ioc 但是如果您仍想使用两个上下文,则可以执行以下操作(myEntity是您要分离/附加的对象):

if (context1.Entry(myEntity).State != EntityState.Detached);
{
    ((IObjectContextAdapter)context1).ObjectContext.Detach(myEntity);
}
context2.MyEntities.Attach(myEntity);

答案 1 :(得分:1)

我得出的结论是,使用ApplyCurrentValues代替附加是最好的(即更容易避免问题)。那是因为当你打电话给Attach时,有几件事我们不知道,但可能会以某种方式通过异常浮出水面。我更喜欢按照我控制的方式做事。

var myMailings = db_creator.Create().Mailing.Where(it => !it.mail_IsSent).ToList();
... // make modifications and retrieve coll a collection of Mailing objects
using (var db = db_creator.Create()) {
  ... // if you want to further modify the objects in coll you should do this before writing it to the context
  foreach (Mailing it in coll) {
    if (it.EntityKey != null) db.GetObjectByKey(it.EntityKey); // load entity
    else db.Mailing.Single(x => x.YourId == it.YourId); // load the entity when EntityKey is not available, e.g. because it's a POCO
    db.Mailing.ApplyCurrentValues(it); // sets the entity state to Modified
  }
  db.SaveChanges();
}

编辑:

我使用Attach测试了此vs的性能。应该注意的是,对于一个带有整数主键的简单表,一个int,一个float和一个用于更新1000个条目的字符串列:差异是2.6s vs 0.27s,所以这个速度要慢得多。

EDIT2:

提出了类似的问题here。答案警告说,将ApplyCurrentValues与timestamp列结合使用。

我还在使用db.GetObjectByKey(it.EntityKey)加载实体时比较了性能,并且性能差异要小得多。 ApplyCurrentValues则需要0.44秒。