Как правильно прикрепить объект записи к контексту данных в Entity Framework? - PullRequest
1 голос
/ 05 октября 2011

Мне нужно переключить контекст данных для некоторых записей.Таким образом, в основном у меня есть db-контекст A и B, я выбираю записи, используя A, затем переключаюсь на B, изменяю записи и сохраняю их.

Когда я вызываю Attach для B, я получаю исключение, что записи используютсямногократный контекст данных, когда я добавляю Detach для A, я получаю исключение, что записи не присоединяются к A.

Так как я могу переключить контекст данных?

Пример

db_creator является создателем контекста БД.Здесь я получаю данные (исправленная версия):

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 is Список записей:

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

Ответы [ 2 ]

1 голос
/ 05 октября 2011

Я пришел к выводу, что лучше (т.е. легче избежать проблем) использовать 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();
}

EDIT:

Я проверил производительность этого с использованием Attach. Следует отметить, что для простой таблицы с целочисленным первичным ключом, int, float и строковым столбцом для обновления 1000 записей: разница составляла 2,6 с против 0,27 с, поэтому это значительно медленнее.

EDIT2:

Подобный вопрос был поднят здесь . Там ответ предупрежден об использовании ApplyCurrentValues ​​вместе со столбцами отметок времени.

Я также сравнил производительность при загрузке объекта с db.GetObjectByKey(it.EntityKey), и там разница в производительности намного меньше. ApplyCurrentValues ​​тогда занимает всего 0,44 с.

1 голос
/ 05 октября 2011

Я рекомендую изменить ваш дизайн и иметь один контекст за раз. (В зависимости от типа проекта это может варьироваться. Обычно в веб-приложениях это один контекст на HTTP-запрос.)

Например, в веб-приложении вы можете сделать это, как показано ниже:

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...