6

Не удается обновить EntitySet: определен DefiningQuery, но отсутствует элемент <UpdateFunction>

28

Я использую 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 ответ(ов)

11

Это обычно происходит по одной из следующих причин:

  • Набор сущностей сопоставлен с представлением базы данных
  • Используется пользовательский запрос к базе данных
  • В таблице базы данных отсутствует первичный ключ

После того как вы устранили одну из этих причин, возможно, вам потребуется обновить данные в дизайнере Entity Framework (или, в качестве альтернативы, удалить сущность и затем добавить её заново), прежде чем ошибка перестанет возникать.

1

Чтобы решить вашу проблему, просто добавьте первичный ключ в таблицу. Вот пример SQL-запроса:

ALTER TABLE <TABLE_NAME>
ADD CONSTRAINT <CONSTRAINT_NAME> PRIMARY KEY(<COLUMN_NAME>)

Это всё! Проблема решена.

0

Это было актуально для меня. Простое удаление привело к другой ошибке. Я следовал шагам из этого поста, за исключением последнего. Для вашего удобства я скопировал 4 шага из поста, которые я выполнил, чтобы решить проблему:

  1. Щёлкните правой кнопкой мыши на файле edmx, выберите "Открыть с помощью", затем выберите XML-редактор.
  2. Найдите сущность в элементе edmx:StorageModels.
  3. Полностью удалите DefiningQuery.
  4. Переименуйте store:Schema="dbo" в Schema="dbo" (в противном случае код будет генерировать ошибку, сообщающую, что имя недействительно).
0

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

0

Обновление: Недавно я получил несколько голосов за это сообщение, поэтому решил уведомить всех, что советы, которые я даю ниже, не являются лучшими. С тех пор как я начал ковыряться с 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 (последнюю на момент написания).

  1. Щелкните правой кнопкой мыши по вашему .edmx файлу в проводнике решений. Выберите «Открыть с помощью...» и затем выберите «XML (текстовый) редактор». Мы будем вручную редактировать сгенерированный код.
  2. Найдите строку, похожую на эту:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" store:Schema="dbo" store:Name="table_name">
  1. Удалите store:Name="table_name" в конце.
  2. Измените store:Schema="whatever" на Schema="whatever".
  3. Посмотрите ниже этой строки и найдите тег <DefiningQuery>. В нем будет большая выборка. Удалите тег и его содержимое.
  4. Теперь ваша строка должна выглядеть примерно так:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" Schema="dbo" />
  1. Нужно внести ещё одно изменение. В вашем файле найдите это:
<EntityType Name="table_name">
  1. Рядом вы, вероятно, увидите какой-то закомментированный текст, предупреждающий о том, что у него не определён первичный ключ, поэтому ключ был выведен, и определение является только для чтения. Вы можете оставить его или удалить. Я его удалил.
  2. Ниже находится тег <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 = (имя клиента дубликата)

С этой предостережением… теперь вы, должно быть, готовы дальше!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь