Удаление LINQ-to-SQL + One-to-Many + DataBinding - PullRequest
9 голосов
/ 21 декабря 2008

Я использую LINQ-to-SQL для загрузки данных из базы данных, в которой есть две таблицы, в отношении «один ко многим» (один рецепт имеет много ингредиентов).

Я загружаю Recipe, а LINQ извлекает объекты Ingredient в EntitySet, который связан с ListBox.

Если я хочу удалить некоторые ингредиенты из рецепта, я получаю сообщение «Была сделана попытка удалить связь между рецептом и ингредиентом. Однако один из внешних ключей отношения (Ingredient.RecipeID) не может быть установлен в ноль.

Я решил эту проблему, используя хорошо известное решение, добавив DeleteOnNull = "true" в файл DBML. Но добавление этого параметра устраняет проблему только при удалении объектов Ingredient, которые были извлечены из БД.

Проблема в объектах Ingredient, которые были созданы в коде (добавлены в рецепт) и добавлены в коллекцию EntitySet Ingredients, а затем удалены, прежде чем вызывается SubmitUpdates. Затем то же самое исключение повторяется снова. Это обычно происходит по новому несохраненному рецепту, когда пользователь добавляет ингредиенты к нему, совершает ошибку и удаляет ингредиент из рецепта. Я добавил DeleteOnNull в обе строки 'Association Name = "Recipe_Ingredient" "в DBML.

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

Ответы [ 7 ]

7 голосов
/ 23 декабря 2008
        try
        {
            // Needed for existing records, but will fail for new records
            yourLINQDataContext.Ingredients.DeleteOnSubmit(ingredient);
        }
        catch (Exception)
        {
            // Swallow
        }

        yourRecipeObject.Ingredients.Remove(ingredient);
3 голосов
/ 23 декабря 2008

Кажется, что вы ищете то, что я искал сам, всего несколько дней назад, когда я спросил " Как мне создать вспомогательные типы данных для WPF-диалога с привязкой к данным с помощью кнопок Ok / Cancel? ».

Ответом является интригующий пост Пола Стовелла, в котором описывается пример адаптера IEditable для Linq to Sql. Это позволит вам создавать желаемую семантику «Применить / Отменить» в обобщенном виде, не полностью отделяя себя от базовых классов, сгенерированных ORm, через полный пользовательский слой.

В целом, это довольно хитрый трюк, который, по сути, позволит вам обойти проблемы, с которыми вы сейчас боретесь. :)

С другой стороны, мне любопытно, почему ваш рецепт соотношения ингредиентов составляет 1: n вместо m: n. Это ради простоты? Я использую чеснок во многих рецептах. :)

2 голосов
/ 31 мая 2010
// Create new entities
Cart c = new Cart();
CartEntry ce = new CartEntry();
ce.Cart = c;

// Delete the entry
c.CartEntries.Remove(ce);
dc.Cartentries.Attach(ce);
dc.CartEntries.DeleteOnSubmit(ce);

// Insert the cart into database
dc.Carts.InsertOnSubmit(c);
dc.SubmitChanges();

Объяснение проблемы: оба объекта, c и ce, не связаны с контекстом данных - они не отслеживаются. EntitySet.Remove () (первая строка удаления) удаляет только связь между c и ce. Хотя c может существовать без связанных записей корзины, ce не может существовать без связанной корзины из-за ограничения внешнего ключа. При отправке изменений в базу данных также обрабатывается отключенный се, вызывая нарушение ограничения и исключение.

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

Для более подробной информации по этому вопросу, проверьте это: http://msdn.microsoft.com/en-us/library/bb546187%28v=VS.100%29.aspx

0 голосов
/ 09 марта 2009

Я сталкиваюсь с подобной проблемой, в качестве обходного пути, мне нужно вызвать DataContext.GetChanges (), тогда, кажется, все снова завоевало популярность:)

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

0 голосов
/ 20 января 2009

У меня была точно такая же проблема. У меня была родительская / дочерняя иерархия, и при добавлении и удалении дочерней сущности без сохранения в базе данных я получил исключение «Была предпринята попытка удалить связь».

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

1. Это создает ошибку

 RetailAccountCustomerCard racc = new RetailAccountCustomerCard();

 Card addedCard = _idc.Cards.Where(c => c.CardId == card.CardId).ToList().First();

 racc.Card = addedCard;

 this.CurrentCustomer.RetailAccountCardsBindingList.Add(racc); 

 // Some code triggered by the user before saving to the db

 CurrentCustomer.RetailAccountCardsBindingList.Remove(racc);

2. Это не создает ошибку

 RetailAccountCustomerCard racc = new RetailAccountCustomerCard();

 racc.CardId = card.CardId;  // note that I have set the Id property not the object

 this.CurrentCustomer.RetailAccountCardsBindingList.Add(racc); 


 // Some code triggered by the user before saving to the db

 CurrentCustomer.RetailAccountCardsBindingList.Remove(racc);

Как ни странно, ошибка, возникающая в 1., указывает, что проблема связана со связью в свойстве RetailAccountCustomerId RetailAccountCustomerCard. ЭТО НИЧЕГО не имеет к объекту Карты, который я добавил. Кажется, что простая установка любого свойства объекта новой сущности вызывает проблему.

NB. Пример 1 работает нормально с точки зрения сохранения, он вызывает проблему только в том случае, если новый объект удаляется перед сохранением.

0 голосов
/ 23 декабря 2008

Спасибо за ваш ответ, я проверю посты и посмотрю, что я могу сделать. Должен сказать, что я удивлен, когда даже вижу, что эта проблема возникает, мне кажется вполне естественным, что можно добавить записи в «кеш» данных, предоставляемый LINQ, затем решить удалить некоторые из них, а затем зафиксировать. Отслеживание изменений должно быть в состоянии справиться с этим. Я только начинаю с LINQ, поэтому я могу сделать глупую ошибку где-то в коде (не первым).

С другой стороны: вы совершенно правы, что чеснок может принадлежать ко многим рецептам (не мои рецепты коктейлей!). Я фактически моделирую это с объектом / таблицей Article. Но для рецепта вам нужны количества. Итак, в моей модели у вас есть рецепт с 1: n ингредиентами, каждый из которых имеет количество, ссылку 1: 1 на статью (которая имеет имя, AlcoholContent и некоторые данные для создания иерархии взаимозаменяемости) и 1: 1 ссылка на единицу (чтобы количество имело смысл). Таким образом, в некотором смысле таблица Ingredient устанавливает отношение M: N между Recipe и Article и одновременно добавляет некоторую дополнительную информацию к каждой отдельной связанной паре.

0 голосов
/ 23 декабря 2008

вам нужно отделить код сохранения от событий в вашем графическом интерфейсе, кажется, что вам не терпится сохранить вещи в БД до того, как пыль осядет, и вы ставите в очередь и удаляете вещи из БД, которые во-первых, никогда не получалось, было бы лучше, если бы вы могли определить момент, когда пользователь будет «фиксировать» свои изменения, и в этот момент обработать полное состояние графического интерфейса - это сэкономит вам кучу спагетти-кода .

Мне также было бы интересно узнать, имеют ли ваши сущности идентификаторы автонумерации или вы используете какой-либо другой механизм идентификаторов. Вы, вероятно, отправляете DELETE в базу данных для еще не переданных записей Ingredient, если они содержат NULL ID, я думаю, что linq может стать неприятным.

Вы подключили текстовый редактор к вашему DataContext.Log, чтобы увидеть, какие виды SQL генерируются непосредственно перед тем, как вы получите исключение?

...