[linq] Cannot add an entity that already exists
От: ascii32  
Дата: 17.05.08 15:55
Оценка:
Здравствуйте. Не могу понять, в чем дело.
Есть две базы, одна обновляется данными из другой.

Процесс обновления происходит следующим образом. Вызывается GetRooms(), выбираются данные из второй базы, производится шаманство и результирующий список попадает на вход функции ReplaceOldValues.

public static List<Room> GetRooms()
{
    return (from r in LinqUtil.Db.Rooms select r).ToList<Room>();
}

public static void ReplaceOldValues(List<Room> rooms)
{
    LinqUtil.Db.Rooms.DeleteAllOnSubmit(GetRooms());
    DAL.LinqUtil.SubmitChanges();
    //var test = GetRooms();
    LinqUtil.Db.Rooms.InsertAllOnSubmit(rooms);
    DAL.LinqUtil.SubmitChanges();
}


Данный подход (в данной реализации) работает исключительно с пустой БД. Если заполняемая база не пуста, то выделеная строчка кидает эксепшн: Cannot add an entity that already exists. Что странно, в режиме отладки, закомментированая переменная test имеет count = 0 (т.е. не содержит записей).

В итоге я получаю, что алгоритм работает через раз. Данных нет — данные появляются — Данных нет (в результате эксепшена) — данные появляются — ..

Подскажите пожалуйста, что я делаю не так.
P.S. Критика алгоритма также приветствуется.
дайте слепому показать вам дорогу.
(Р. Бредбери "Смерть — дело одинокое")
Re: [linq] Cannot add an entity that already exists
От: Lloyd Россия  
Дата: 17.05.08 22:48
Оценка:
Здравствуйте, ascii32, Вы писали:

A>Данный подход (в данной реализации) работает исключительно с пустой БД. Если заполняемая база не пуста, то выделеная строчка кидает эксепшн: Cannot add an entity that already exists. Что странно, в режиме отладки, закомментированая переменная test имеет count = 0 (т.е. не содержит записей).


А если после DeleteAllOnSubmit вызвать SubmitChanges?
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[2]: [linq] Cannot add an entity that already exists
От: ascii32  
Дата: 18.05.08 09:29
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Здравствуйте, ascii32, Вы писали:


L>А если после DeleteAllOnSubmit вызвать SubmitChanges?


А я вызываю.

LinqUtil.Db.Rooms.DeleteAllOnSubmit(GetRooms());
DAL.LinqUtil.SubmitChanges();


Правда для ясности надо было DAL (пространство имен) убрать (делал пример ошибки в спешке).

Вообще, удалось забороть проблему, но она от этого непонятней не стала. Исходный код работает как ожидалось, если после первого SubmitChanges (после удаления) освободить DataContext и создать заново .
дайте слепому показать вам дорогу.
(Р. Бредбери "Смерть — дело одинокое")
Re: [linq] Cannot add an entity that already exists
От: Mace Windu  
Дата: 19.05.08 07:32
Оценка:
Здравствуйте, ascii32, Вы писали:

Похоже что кроме пересоздания контекста ничего сделать нельзя

На мой поверхностный взгляд тут какие-то косяки в реализации трекинга объектов в LINQ to SQL.
Вариант 1. Ты добавляешь объект, который ты достал из базы. Твое исключение вылетает на следующем коде в InsertOnSubmit:
if (trackedObject == null)
    {
        this.context.Services.ChangeTracker.Track(entity).ConvertToNew();
    }
    else if (trackedObject.IsWeaklyTracked)
    {
        trackedObject.ConvertToNew();
    }
    else if (trackedObject.IsDeleted)
    {
        trackedObject.ConvertToPossiblyModified();
    }
    else if (trackedObject.IsRemoved)
    {
        trackedObject.ConvertToNew();
    }
    else if (!trackedObject.IsNew)
    {
        throw Error.CantAddAlreadyExistingItem(); // вот тут вылетает исключение. Хотя если подумать, то правильнее было бы если бы сработал if с IsDeleted
    }


Вариант 2. Я попробовал создать новый объект вместо reusing старого (но с существовавшим ранее ключем). Всё равно вылетает исключение но уже на сабмите в

ChangeProcessor.TrackUntrackedObjects(MetaType type, object item, Dictionary<object, object> visited)
...

 object obj3 = this.services.InsertLookupCachedObject(trackedObject.Type, item);
                if (obj3 != item)
                {
                    TrackedObject obj4 = this.tracker.GetTrackedObject(obj3);
                    if (!obj4.IsDeleted && !obj4.CanInferDelete())
                    {
                        if (!obj4.IsDead)
                        {
                            throw new DuplicateKeyException(item, Strings.CantAddAlreadyExistingKey); // вот тут исключение наше
                        }
                    }

Как видно LINQtoSQL испытывает тут те же проблемы с определением что obj4 (т.е. старый Room с таким же ключем был успешно удален)

Короче спасибо за вопрос, будем иметь ввиду такие тонкости
... << RSDN@Home 1 alpha 4 rev. 0>>
Re[2]: [linq] Cannot add an entity that already exists
От: Mace Windu  
Дата: 19.05.08 07:39
Оценка: 2 (1)
Здравствуйте, Mace Windu, Вы писали:

Итого: если через контекст был удален объект, добавлять объект с тем же ключем придется через новый контекст. Ничего в принципе страшного в этом нет.
... << RSDN@Home 1 alpha 4 rev. 0>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.