Linq to sql add / update разными способами с разными данными - PullRequest
2 голосов
/ 19 марта 2010

У меня есть методы Add () и Update (), которые создают текстовый текст и возвращают созданный / обновленный объект.

В моем модульном тесте я сначала вызываю Add (), делаю некоторые вещи, а затем вызываю Update (). Проблема в том, что Update () завершается ошибкой с исключением:

  System.Data.Linq.DuplicateKeyException: Cannot add an entity with a key that is already in use..

Я понимаю проблему, но хочу знать, что с этим делать? Я прочитал немного о том, как обрабатывать несколько объектов Datacontext и из того, что я слышал, это нормально.

Я понимаю, что сущность все еще прикреплена к текстовому соединению в Add (), но мне нужно выяснить, как это решить?

Заранее спасибо

Ответы [ 4 ]

5 голосов
/ 19 марта 2010

Если говорить прямо, то с точки зрения дизайна неправильно раскручивать DataContext экземпляров в Add и Update методах.

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

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

public class FooRepository
{
    private MyDataContext context;

    public FooRepository(MyDataContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        this.context = context;
    }

    public void Add(Foo foo)
    {
        context.FooTable.InsertOnSubmit(foo);
    }

    public void Update(Foo foo)
    {
        Foo oldFoo = context.FooTable.Single(f => f.ID == foo.ID);
        oldFoo.Bar = foo.Bar;
        oldFoo.Baz = foo.Baz;
    }
}

Затем, откуда вы выполняете свои обновления:

Foo fooToSave = GetFooFromWherever();
using (MyDataContext context = new MyDataContext(...))
{
    FooRepository repository = new FooRepository(context);
    repository.Save(fooToSave);
    context.SubmitChanges();
} // Done

Этот шаблон можно использовать и использовать повторно, и вы можете объединить несколько репозиториев в одну «транзакцию»; вы никогда не столкнетесь с какими-либо проблемами. Вот как на самом деле предполагалось использовать DataContext, который включает в себя шаблон единицы работы.

Между прочим, при проектировании хранилища принято пытаться абстрагироваться от громоздкой Insert / Update семантики и просто предоставлять метод Save:

public void Save(Foo foo)
{
    if (foo.ID == default(int))
    {
        Insert(foo);
    }
    else
    {
        Update(foo);
    }
}

Таким образом, вам не всегда нужно беспокоиться о том, вставили ли вы уже Foo.

возможно принудить Linq к SQL для работы с отсоединенными сущностями, но удачи вам в том, чтобы иметь дело с сущностями, которые уже прикреплены к другой контекст . И даже в отдельном случае, это действительно довольно громоздко, вам нужно иметь поля меток времени на всех ваших таблицах / сущностях или начать возиться со свойствами проверки версии / автосинхронизации - это не стоит IMO, просто спроектируйте свои репозитории для использования один контекст для каждого экземпляра и для совместного использования экземпляров контекста.

0 голосов
/ 19 марта 2010

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

Если вы работаете с различными контекстами данных, рекомендуется создать новый объект сущности и выполнить добавление в этом первом контексте данных, затем извлечь эту запись, используя другой контекст данных, и выполнить обновления с новым объектом сущности .

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

0 голосов
/ 19 марта 2010

Вам нужно будет вызвать Attach() в экземпляре таблицы контекста обновления для объекта, возвращенного из Add(), например, updateContext.Products.Attach(product).

Если подключение выполнено успешно, вы можете обновить сущность, и LINQ to SQL будет «знать», что нужно выполнить обновление, а не вставку.

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

Я бы просто загрузил новую сущность в контекст обновления из базы данных, используя SingleOrDefault() или аналогичный, а затем работал бы над этим, предоставляя вам ту же функциональность, но гораздо менее глючную.

0 голосов
/ 19 марта 2010

Чтобы обновить сущность, которая присоединена к другому контексту данных, вам сначала необходимо отсоединить ее от контекста, а затем присоединить ее к другому контексту. Один из способов отсоединения объекта заключается в следующем:

object ICloneable.Clone()
    {
        var serializer = new DataContractSerializer(GetType());
        using (var ms = new System.IO.MemoryStream())
        {
            serializer.WriteObject(ms, this);
            ms.Position = 0;
            return serializer.ReadObject(ms);
        }
    }
...