LINQ to SQL - Сохранить объект, не создавая новый DataContext? - PullRequest
2 голосов
/ 20 апреля 2010

Я получаю эту ошибку

Невозможно добавить объект с ключом, который уже используется

когда я пытаюсь сохранить элемент

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Item item)
{
  Global.DataContext.Items.Attach(item);
  Global.DataContext.SubmitChanges();

  return View(item);
}

Это потому, что я не могу прикрепить элемент к глобальному DataContext.

Можно ли сохранить элемент, не создавая новый DataContext и не назначая каждое поле элемента вручную?

(я очень новичок в LINQ)

РЕДАКТИРОВАТЬ: я понял, статический DataContext будет вызывать проблемы благодаря комментариям ниже, теперь это так

public static AbcDataContext DataContext
{
  get
  {
    if (!HttpContext.Current.Items.Contains("DataContext"))
      HttpContext.Current.Items["DataContext"] = new AbcDataContext(ConnectionString);
    return (AbcDataContext)HttpContext.Current.Items["DataContext"];
  }
}

(Рекс может не согласиться с этим, но я не могу быть обеспокоен изменением всего кода на данный момент - может быть позже)

Ответы [ 5 ]

4 голосов
/ 20 апреля 2010

Нет глобального / статического DataContext, который настраивает себя на боль. DataContext должен представлять одну логическую транзакцию («войти, сделать x / y / z и выйти»). Они дешевы в создании и легко утилизируются; нет абсолютно никаких причин пытаться свести их к минимуму, а тем более сохранить глобальный / статический.

3 голосов
/ 20 апреля 2010

Предположим, что первичным ключом вашего класса Item является ItemId.

Предположим, ItemID для экземпляра, который вы пытаетесь обновить, равен 5.

DataContext видел исходное состояние для ItemID 5, поэтому он не позволит вам Attach (). http://msdn.microsoft.com/en-us/library/bb300517.aspx

В этой версии Attach, сущность предполагается, что в его первоначальном значении государство. После вызова этого метода вы может затем обновить свои поля, для пример с дополнительными данными, отправленными из клиент.

В LinqToSql есть три обычных способа обновления.

Если параметр этого метода редактирования был первоначально загружен из DataContext, то все, что вам нужно сделать, это:

public ActionResult Edit(Item item) 
{
  Global.DataContext.SubmitChanges(); 
  return View(item); 
} 

DataContext отслеживает изменения в загруженных им объектах. Как неприятный побочный эффект, любые измененные объекты, которые были загружены DataContext, также будут обновлены. Это серьезная причина не использовать DataContext одного уровня приложения.

Если параметр для этого метода Edit был новым в вашем коде, загружен другим DataContext или передан в ваш код (другими словами, экземпляр не имеет прикрепленного DataContext), то вы можете сделать одно из следующих:

public ActionResult Edit(Item item) 
{
  using(MyDataContext dc = new MyDataContext())
  {
//this new DataContext has never heard of my item, so I may Attach.
    dc.Items.Attach(item);
//this loads the database record in behind your changes
// to allow optimistic concurrency to work.
//if you turn off the optimistic concurrency in your item class
// then you won't have to do this
    dc.Refresh(item, KeepCurrentValues);
    dc.SubmitChanges(); 
  }
  return View(item); 
} 

public ActionResult Edit(Item item) 
{
  original = Global.DataContext.Items.Single(x => x.ItemID = item.ItemID)
  //play the changes against the original object.
  original.Property1 = item.Property1;
  original.Property2 = item.Property2;
  Global.DataContext.SubmitChanges(); 
  return View(item); 
} 

Ответив на ваш вопрос, позвольте мне повторить озабоченность, высказанную другими по поводу использования статического DataContext. Это плохая практика и идет вразрез с предполагаемым использованием Microsoft класса DataContext. http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx

В общем случае экземпляр DataContext рассчитан на одну единицу работа "однако ваше приложение определяет этот термин. DataContext - это легкий и не дорогой Создайте. Типичный LINQ to SQL приложение создает DataContext экземпляры в области видимости метода или как член недолговечных классов, которые представляют собой логический набор связанных операции с базой данных.

1 голос
/ 21 апреля 2010

Обсуждение DataContext. Заметьте, я не комментирую ваш код.

DataContexts реализует IDisposable, и поэтому вы должны избавляться от контекста данных, когда он больше не нужен. Ваш веб-сайт достаточно хорошо работает в разработке, но в процессе работы вы будете заколочены. С таким же успехом вы можете сделать это прямо перед тем, как ваш код станет слишком укоренившимся, и его изменение станет большой проблемой. В лучшем случае вы просто выработаете вредные привычки.

Лучшая альтернатива тому, что вы написали, - иметь собственный базовый класс контроллера, который управляет временем жизни для вас.

public class MyBaseController : System.Web.Mvc.Controller
{
    private AbcDataContext abcDataContext;

    protected AbcDataContext DataContext
    {
        get 
        {   // lazy-create of DataContext
            if (abcDataContext == null)
                abcDataContext = new AbcDataContext(ConnectionString);

            return abcDataContext;
        }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
        {
            if( abcDataContext != null )
                abcDataContext.Dispose();
        }
    }
}

, что позволяет вам сделать

public class MyController : MyBaseController
{
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(Item item)
    {
        DataContext.Items.Attach(item);
        DataContext.SubmitChanges();

        return View(item);
    }
}

Хотя это работает, я лично считаю, что это может стать раздражающим и хитрым.


Лучший: если вы собираетесь следовать MVC, как и положено, вы должны заполнить модель полностью, а не полагаться на объекты с отложенной загрузкой. Лучший способ добиться этого - как можно быстрее избавиться от вашего DataContext.

Как правило, мы применяем это на уровне кода по следующей схеме:

using( var dc = new AbcDataContext(ConnectionString))
{
    var itemUpdater = new ItemUpdater(dc);
    item = itemUpdater.Update(item);
}
return View(item);

Идея состоит в том, что вы получите ObjectDisposedException, если ваше представление попытается получить какие-либо дополнительные данные с помощью отложенной загрузки.

1 голос
/ 20 апреля 2010

статический глобальный DataContext? Если я правильно понимаю ваш вопрос, это приведет к тому, что все, кто подключится к вашему приложению, будут использовать один и тот же контекст данных, что вызовет много проблем с безопасностью / синхронизацией. Избегайте этого.

0 голосов
/ 20 апреля 2010

Согласно этой дискуссии об этой же проблеме , похоже, что это ошибка отображения типов, которую можно обойти, если удалить класс Item в конструкторе и просто перетащить через стол снова в конструктор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...