Не удается обновить EntitySet: определен DefiningQuery, но отсутствует элемент <UpdateFunction>
Я использую Entity Framework 1 с .NET 3.5.
Я выполняю простую операцию:
var roomDetails = context.Rooms.ToList();
foreach (var room in roomDetails)
{
room.LastUpdated = DateTime.Now;
}
Однако, когда я пытаюсь выполнить:
context.SaveChanges();
я получаю следующую ошибку:
Unable to update the EntitySet - because it has a DefiningQuery and no
element exists in the element to support the current operation.
Я выполняю множество обновлений в контексте, и никаких проблем не возникает, но только при попытке обновить этот конкретный объект.
Все мои поиски показывают одно и то же: проблема в том, что не объявлен первичный ключ для сущности, которую я пытаюсь обновить. Но, к сожалению, у меня действительно объявлен первичный ключ...
Есть ли какие-то идеи, как решить эту проблему?
5 ответ(ов)
Это обычно происходит по одной из следующих причин:
- Набор сущностей сопоставлен с представлением базы данных
- Используется пользовательский запрос к базе данных
- В таблице базы данных отсутствует первичный ключ
После того как вы устранили одну из этих причин, возможно, вам потребуется обновить данные в дизайнере Entity Framework (или, в качестве альтернативы, удалить сущность и затем добавить её заново), прежде чем ошибка перестанет возникать.
Чтобы решить вашу проблему, просто добавьте первичный ключ в таблицу. Вот пример SQL-запроса:
ALTER TABLE <TABLE_NAME>
ADD CONSTRAINT <CONSTRAINT_NAME> PRIMARY KEY(<COLUMN_NAME>)
Это всё! Проблема решена.
Это было актуально для меня. Простое удаление привело к другой ошибке. Я следовал шагам из этого поста, за исключением последнего. Для вашего удобства я скопировал 4 шага из поста, которые я выполнил, чтобы решить проблему:
- Щёлкните правой кнопкой мыши на файле edmx, выберите "Открыть с помощью", затем выберите XML-редактор.
- Найдите сущность в элементе edmx:StorageModels.
- Полностью удалите DefiningQuery.
- Переименуйте
store:Schema="dbo"
вSchema="dbo"
(в противном случае код будет генерировать ошибку, сообщающую, что имя недействительно).
Обратите внимание, что, возможно, у вашего сущности есть первичный ключ, но в вашей базе данных нет первичного ключа.
Обновление: Недавно я получил несколько голосов за это сообщение, поэтому решил уведомить всех, что советы, которые я даю ниже, не являются лучшими. С тех пор как я начал ковыряться с Entity Framework на старых безключевых базах данных, я пришёл к выводу, что лучший способ — это использовать обратный подход Code First. Существует несколько хороших статей о том, как это сделать. Просто следуйте им, а когда вам понадобится добавить ключ, используйте аннотации данных, чтобы «сымитировать» ключ.
Например, допустим, я знаю, что в таблице Orders
, хотя она и не имеет первичного ключа, гарантировано будет только один номер заказа на клиента. Поскольку это первые два столбца таблицы, я настрою классы Code First следующим образом:
[Key, Column(Order = 0)]
public Int32? OrderNumber { get; set; }
[Key, Column(Order = 1)]
public String Customer { get; set; }
Таким образом, вы фактически «заставляете» EF поверить, что существует кластерный ключ, составленный из OrderNumber и Customer. Это позволит вам выполнять вставку, обновление и т.д. в вашей безключевой таблице.
Если вы не очень знакомы с обратным подходом Code First, найдите хороший учебник по Entity Framework Code First. Затем найдите один по Reverse Code First (что подразумевает выполнение Code First с существующей базой данных). После этого просто вернитесь сюда и посмотрите на мои советы по ключам ещё раз. 😃
Оригинальный ответ:
Во-первых, как уже упоминали другие, лучший вариант — это добавить первичный ключ в таблицу. Точка. Если вы можете это сделать, не читайте дальше.
Но если вы не можете или просто ненавидите себя, есть способ сделать это без первичного ключа.
В моем случае я работал с устаревшей системой (изначально это были плоские файлы на AS400, которые были перенесены в Access, а затем в T-SQL). Поэтому мне пришлось найти решение. Вот что сработало для меня, используя Entity Framework 6.0 (последнюю на момент написания).
- Щелкните правой кнопкой мыши по вашему .edmx файлу в проводнике решений. Выберите «Открыть с помощью...» и затем выберите «XML (текстовый) редактор». Мы будем вручную редактировать сгенерированный код.
- Найдите строку, похожую на эту:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" store:Schema="dbo" store:Name="table_name">
- Удалите
store:Name="table_name"
в конце. - Измените
store:Schema="whatever"
наSchema="whatever"
. - Посмотрите ниже этой строки и найдите тег
<DefiningQuery>
. В нем будет большая выборка. Удалите тег и его содержимое. - Теперь ваша строка должна выглядеть примерно так:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" Schema="dbo" />
- Нужно внести ещё одно изменение. В вашем файле найдите это:
<EntityType Name="table_name">
- Рядом вы, вероятно, увидите какой-то закомментированный текст, предупреждающий о том, что у него не определён первичный ключ, поэтому ключ был выведен, и определение является только для чтения. Вы можете оставить его или удалить. Я его удалил.
- Ниже находится тег
<Key>
. Это то, что Entity Framework будет использовать для выполнения вставок/обновлений/удалений. ПОЭТОМУ УБЕДИТЕСЬ, ЧТО ВЫ ЭТО ДЕЛАЕТЕ ПРАВИЛЬНО. Свойство (или свойства) в этом теге должно указывать на уникально идентифицируемую строку. Например, допустим, я знаю, что в таблицеorders
, хотя у неё и нет первичного ключа, гарантировано будет только один номер заказа на клиента.
Итак, у меня это выглядит так:
<EntityType Name="table_name">
<Key>
<PropertyRef Name="order_numbers" />
<PropertyRef Name="customer_name" />
</Key>
Правда, не делайте это неправильно. Давайте предположим, что, хотя дубликатов быть не должно, как-то в мою систему попадают две строки с одинаковым номером заказа и именем клиента. Ой! Вот что я получаю за то, что не использовал ключ! Поэтому я использую Entity Framework, чтобы удалить один из них. Поскольку я знаю, что дубликат — это единственный заказ, сделанный сегодня, я делаю это:
var duplicateOrder = myModel.orders.First(x => x.order_date == DateTime.Today);
myModel.orders.Remove(duplicateOrder);
Как вы думаете, что произошло? Я только что удалил оба — и дубликат, и оригинал! Это произошло потому, что я сказал Entity Framework, что order_number/customer_name является моим первичным ключом. Так что, когда я сказал удалить duplicateOrder, то в фоновом режиме произошло что-то вроде:
DELETE FROM orders
WHERE order_number = (номер заказа дубликата)
AND customer_name = (имя клиента дубликата)
С этой предостережением… теперь вы, должно быть, готовы дальше!
В C# в чем разница между public, private, protected и отсутствием модификатора доступа?
Ввод ключей вручную с помощью Entity Framework
Лучшая настройка непрерывной интеграции для одиночного разработчика (.NET) [закрыт]
Как запустить модульные тесты на платформе x64?
Использование номера версии веб-приложения из сборки (ASP.NET/C#)