Невозможно обновить EntitySet - потому что у него есть DefiningQuery и отсутствует элемент <UpdateFunction>

534

Я использую Entity Framework 1 с .net 3.5.

Я делаю что-то простое, как это:

var roomDetails = context.Rooms.ToList();

foreach (var room in roomDetails)
{        
   room.LastUpdated = DateTime.Now;
}

Я получаю эту ошибку, когда я пытаюсь сделать:

 context.SaveChanges();

Я получаю ошибку:

Невозможно обновить EntitySet - потому что у него есть DefiningQuery, а в элементе <ModificationFunctionMapping> отсутствует элемент <UpdateFunction> для поддержки текущей операции.

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

Все мои поиски обнаруживают одно и то же, что у сущности, которую я пытаюсь обновить, нет объявленного первичного ключа. Но, увы, у меня есть первичный ключ объявлен ...

iKode
источник
61
Я сделал ошибку, на столе не было первичного ключа, спасибо за ваше время! Приносим извинения за неудобства!
iKode
1
Только что случилось со мной - вероятно, создал 1000 таблиц с первичными ключами и забыл одну - сообщение об исключении мало помогает
Питер Маннингс
1
отлично. действительно я забыл добавить первичный ключ в таблицу. Будем стараться быть осторожными)
AEMLoviji

Ответы:

1024

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

  • Набор объектов отображается из представления базы данных
  • Пользовательский запрос к базе данных
  • Таблица базы данных не имеет первичного ключа

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

Ладислав Мрнка
источник
2
Не забудьте также изменить store: Schema на Schema для этого EntitySet, если у вас все еще есть проблемы.
Джефф
53
Затем удалите и заново создайте объект, потому что обновление не работает прямо в конструкторе EF.
Suncat2000
48
ПК был ответом. Спасибо!
nrod
1
Обновление в конструкторе EF работало нормально после добавления первичного ключа в базу данных. Использование EF 5.0 и .net 4.0
StillLearnin
1
Тоже самое ! Thx ... пришлось убрать таблицу и заново добавить в EF, чтобы заставить ее принять хотя
ajzeffer
90

Просто добавьте первичный ключ к таблице. Вот и все. Задача решена.

ALTER TABLE <TABLE_NAME>
ADD CONSTRAINT <CONSTRAINT_NAME> PRIMARY KEY(<COLUMN_NAME>)
Джебастин Дж
источник
13
и не забудьте нажать «Обновить модель из базы данных» в своем файле .edmx
Башар Абу Шамаа
@BasharAbuShamaa этот ответ недействителен без этой детали.
Келан Крумме
66

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

  1. Щелкните правой кнопкой мыши на файле EDMX, выберите Открыть с помощью, редактор XML
  2. Найдите объект в элементе edmx: StorageModels
  3. Удалить DefiningQuery полностью
  4. Переименование store:Schema="dbo"в Schema="dbo"( в противном случае код выдаст ошибку , сказав , что имя является недействительным)
Кавита Редди
источник
Большое спасибо - это именно то, что исправило мою проблему. Довольно неприятно, что это не было исправлено в EF. И, довольно удивительно, что ты понял это!
Велосипед Дейв
Я попытался удалить объект и добавить его заново. Пересобираем. Очистка. У меня ничего не получалось, кроме этого.
vintastic
1
Это решило мою проблему, но я не знаю, как вы пришли к ответам и почему ваше предложение решило проблему.
swcraft
Что произойдет, если вам нужно обновить модель базы данных? Я сделал «Обновление модели из базы данных», и это сделало мою модель совершенно непригодной для использования. Мне пришлось отменить и начать все сначала. Есть ли способ обойти это?
Гэри
Это действительно странная проблема. Есть ли какая-либо информация о том, как возникает эта проблема, чтобы ее избежать? Тем не менее - это помогло
r3dst0rm
41

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

Маджид
источник
1
Как побороть, если мы не можем изменить таблицу базы данных?
Кай Хартманн
Если вы можете изменить таблицу БД на первичный ключ, тогда генератор кода перестанет делать те же ошибки, удаление ключа из EF вызовет много других проблем.
Крис Шаллер
30

ОБНОВЛЕНИЕ: Я получил несколько голосов по этому поводу в последнее время, поэтому я решил, что я дам людям знать, что совет, который я даю ниже, не самый лучший. С тех пор как я начал заниматься созданием Entity Framework со старыми базами данных без ключей, я пришел к выводу, что лучшее, что вы можете сделать BY FAR, это сделать это с помощью обратного кода в первую очередь. Есть несколько хороших статей о том, как это сделать. Просто следуйте им, а затем, когда вы захотите добавить ключ, используйте аннотации данных, чтобы «подделать» ключ.

Например, скажем, я знаю, что моя таблица Orders, хотя она не имеет первичного ключа, гарантирует, что когда-либо будет только один номер заказа на одного клиента. Так как это первые два столбца в таблице, я бы настроил первые классы кода так:

    [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 (последний на NuGet на момент написания статьи).

  1. Щелкните правой кнопкой мыши по файлу .edmx в обозревателе решений. Выберите «Открыть с помощью ...», а затем выберите «XML (Text) Editor». Мы собираемся вручную редактировать сгенерированный код здесь.

  2. Посмотрите на строку, как это:
    <EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" store:Schema="dbo" store:Name="table_nane">

  3. Снять store:Name="table_name"с конца.

  4. Изменить store:Schema="whatever"наSchema="whatever"

  5. Посмотрите под этой строкой и найдите <DefiningQuery>тег. В нем будет большое старое утверждение. Удалите тег и его содержимое.

  6. Теперь ваша строка должна выглядеть примерно так:
    <EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" Schema="dbo" />

  7. У нас есть что-то еще, чтобы изменить. Просмотрите ваш файл и найдите это:
    <EntityType Name="table_name">

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

  9. Ниже <Key>тег. Это то, что Entity Framework будет использовать для вставки / обновления / удаления. ТАК УБЕДИТЕСЬ, ЧТО ВЫ ДЕЛАЕТЕ ЭТО ПРАВО. Свойство (или свойства) в этом теге должно указывать на уникально идентифицируемую строку. Например, скажем, я знаю, что моя таблица orders, хотя она не имеет первичного ключа, гарантирует, что когда-либо будет только один номер заказа на одного клиента.

Так что мой выглядит так:

<EntityType Name="table_name">
              <Key>
                <PropertyRef Name="order_numbers" />
                <PropertyRef Name="customer_name" />
              </Key>

Серьезно, не делай этого неправильно. Допустим, что, хотя никогда не должно быть дубликатов, в мою систему попадают две строки с одинаковым номером заказа и именем клиента. Whooops! Вот что я получаю за то, что не использую ключ! Поэтому я использую Entity Framework, чтобы удалить один. Поскольку я знаю, что дубликат - единственный заказ на сегодня, я делаю это:

var duplicateOrder = myModel.orders.First(x => x.order_date == DateTime.Today);
myModel.orders.Remove(duplicateOrder);

Угадай, что? Я просто удалил как дубликат, так и оригинал! Это потому, что я сказал Entity Framework, что order_number / cutomer_name был моим основным ключом. Поэтому, когда я сказал ему удалить duplicateOrder, то, что он делал в фоновом режиме, было что-то вроде:

DELETE FROM orders
WHERE order_number = (duplicateOrder's order number)
AND customer_name = (duplicateOrder's customer name)

И с этим предупреждением ... теперь тебе нужно идти!

Pharylon
источник
Нашел этот ответ после того, как нашел такое же решение проблемы. Определенно правильный ответ! Только определение первичного ключа, как указано в других ответах, не поможет во многих случаях.
Обл Тобл
19

Это также может произойти, если модель данных устарела.

Надеюсь, это спасет кого-то еще от разочарования :)

mob1lejunkie
источник
6

Я получал то же сообщение об ошибке, но в моем сценарии я пытался обновить сущности, полученные из отношения «многие ко многим», используя PJT (Pure Join Table).

Прочитав другие посты, я решил исправить это, добавив дополнительное поле PK в таблицу соединений ... Однако, если вы добавляете столбец PK в таблицу соединений, это больше не PJT, и вы теряете все преимущества структуры сущностей, такие как автоматическое сопоставление отношений между сущностями.

Таким образом, решение в моем случае состояло в том, чтобы изменить таблицу соединений в БД, чтобы сделать PK, включающий ОБА из столбцов внешних идентификаторов.

Керри Рэндольф
источник
Это как генерация EDMX всегда работал? Я привык работать с Code First, который не требует PK на чистой таблице соединений.
Майкл Хорнфек,
4

может произойти ошибка, если ваша таблица не имеет первичного ключа, в этом случае таблица «только для чтения», а команда db.SaveChanges () всегда будет выдавать ошибку

Ruben.sar
источник
4

Установите Primary Key, затем сохраните Table и Refresh, затем перейдите в Model.edmx, удалите Table и получите снова.

Али Раза
источник
3

так что это правда, просто добавьте первичный ключ

Примечание: убедитесь, что когда вы обновляете свою диаграмму EF из базы данных, которую вы указываете на нужную базу данных, в моем случае строка подключения указывала на локальную БД, а не на современную БД разработчика, школьник ошибка, я знаю, но я хотел опубликовать это, потому что это может быть очень сложно, если вы уверены, что вы добавили первичный ключ, и вы все еще получаете ту же ошибку

Spyder
источник
2

Я была такая же проблема. Как говорилось в этой ветке, в моей таблице не было PK, поэтому я установил PK и запустил код. Но, к сожалению, ошибка пришла снова. Затем я удалил соединение с БД (удалите файл .edmx в папке Model в обозревателе решений) и воссоздал его. Ошибка ушла после этого. Спасибо всем за то, что поделились своим опытом. Это сэкономит много времени.

Намаль
источник
1

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

Оказывается, на столе не было никаких ключей. EF генерировал модель со множеством ключей. Мне нужно было добавить первичный ключ в таблицу db в SQL, а затем обновил мою модель в VS.

Это исправило это для меня.

застывший
источник
1

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

USE [YourDatabaseName]
GO

Alter table  [dbo].[YourTableNname]
Add Constraint PK_YourTableName_UniqueID Primary Key Clustered (UniqueID);
GO
JessS
источник
1

введите описание изображения здесь

В моем случае забыли определить первичный ключ к таблице. Поэтому назначьте, как показано на рисунке, и обновите таблицу из «Обновить модель из базы данных» из файла .edmx. Надеюсь, это поможет!

Йогеш Дангре
источник
0

Добавление первичного ключа работало для меня тоже!

Как только это будет сделано, вот как обновить модель данных, не удаляя ее:

Щелкните правой кнопкой мыши на странице дизайнера сущностей edmx и выберите «Обновить модель из базы данных».

Абхишек Пуджары
источник
0

У меня была точно такая же проблема, к сожалению, добавление первичного ключа не решает проблему. Итак, вот как я решаю свой:

  1. Убедитесь, что у вас есть primary keyтаблица, чтобы я изменил свою таблицу и добавил первичный ключ.
  2. Delete the ADO.NET Entity Data Model (файл EDMX), где я использую для сопоставления и подключения к моей базе данных.
  3. Add again a new file of ADO.NET Entity Data Model для соединения с моей базой данных и для отображения свойств моей модели.
  4. Clean and rebuild the solution.

Задача решена.

Вилли Дэвид младший
источник
0

просто добавьте первичный ключ к вашей таблице, а затем воссоздайте свой EF

Sulyman
источник
0

Мне просто пришлось удалить таблицу из модели и снова обновить модель, вернув таблицу обратно. Я предполагаю, что первичный ключ был создан после того, как таблица была введена в модель.

dangalg
источник
0

У меня возникла эта проблема, и я считаю, что она была вызвана тем, что я удалил первичный ключ индекса для моих таблиц и заменил его индексом для некоторых других полей в таблице.

После того как я удалил индекс первичного ключа и обновил edmx, вставки перестали работать.

Я обновил таблицу до более старой версии, обновил edmx, и все снова работает.

Я должен отметить, что когда я открыл EDMX, чтобы устранить эту проблему, проверил, был ли определен первичный ключ, был. Так что ни одно из вышеперечисленных предложений не помогло мне. Но обновление индекса по первичному ключу, похоже, сработало.

armstb01
источник
0

Откройте файл .edmx в редакторе XML, а затем удалите тег из тега, а также измените store: Schema = "dbo" на Schema = "dbo" и перестройте решение, теперь ошибка устранится, и вы сможете сохранить данные.

Шарад Тивари
источник
0

Я нашел оригинальный ответ обновления файла .edmx лучше всего в моей ситуации. Я просто не был рад изменению модели каждый раз, когда она обновлялась из базы данных. Вот почему я написал дополнительный файл текстового шаблона, который автоматически вызывается после изменения модели - точно так же, как объекты создаются заново. Я размещаю это здесь в этом комментарии. Чтобы это работало, убедитесь, что вы назвали его как {имя модели} .something.tt, и сохраните его в той же папке, что и ваша папка .edmx. Я назвал это {имя модели} .NonPkTables.tt. Он не создает файл сам по себе из-за неверного определения расширения файла во второй строке. Не стесняйтесь использовать.

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ output extension="/" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq"#>
<#@ assembly name="%VS120COMNTOOLS%..\IDE\EntityFramework.dll" #>
<#@ assembly name="%VS120COMNTOOLS%..\IDE\Microsoft.Data.Entity.Design.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Windows.Forms" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Data.Entity.Core.Metadata.Edm" #>
<#@ import namespace="System.Data.Entity.Core.Mapping" #>
<#@ import namespace="System.CodeDom" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#@ import namespace="Microsoft.CSharp"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="System.Diagnostics" #>

<#
    string modelFileName= this.Host.TemplateFile.Split('.')[0] + ".edmx";
    string edmxPath = this.Host.ResolvePath( modelFileName );

    // MessageBox.Show( this.Host.TemplateFile + " applied." );
    var modelDoc = XDocument.Load(edmxPath);
    var root = modelDoc.Root;
    XNamespace nsEdmx = @"http://schemas.microsoft.com/ado/2009/11/edmx";
    XNamespace ns = @"http://schemas.microsoft.com/ado/2009/11/edm/ssdl";

    var runtime = root.Elements(nsEdmx + "Runtime").First();
    var storageModels = runtime.Elements(nsEdmx + "StorageModels").First();
    XNamespace nsStore = @"http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator";

    var schema = storageModels.Elements(ns + "Schema").First();
    XNamespace nsCustomAnnotation = @"http://schemas.microsoft.com/ado/2013/11/edm/customannotation";

    var entityTypes = schema.Nodes().OfType<XComment>().Where(c => c.Value.Contains("warning 6002: The table/view"));
    bool changed = false;

    foreach (var node in entityTypes)
    {
        var element = node.ElementsAfterSelf().First();
        string entityName = element.Attribute("Name").Value;

        // Find EntitySet in EntityContainer.
        var entityContainer = schema.Elements(ns + "EntityContainer").First();
        var entitySet = entityContainer.Elements(ns + "EntitySet").First(s => s.Attribute("Name").Value == entityName);

        // Change "store:Schema" attribute to "Schema" attribute.
        var attribute = entitySet.Attribute(nsStore + "Schema");

        if (attribute != null)
        {
            string schemaName = entitySet.Attribute(nsStore + "Schema").Value;
            entitySet.Attribute(nsStore + "Schema").Remove();
            entitySet.Add(new XAttribute("Schema", schemaName));
            changed |= true;
        }

        // Remove the DefiningQuery element.
        var definingQuery = entitySet.Element(ns + "DefiningQuery");

        if (definingQuery != null)
        {
            definingQuery.Remove();
            changed |= true;        
            Debug.WriteLine(string.Format("Removed defining query of EntitySet {0}.", entityName));
        }
    }

    if (changed)
        modelDoc.Save(edmxPath);
#>
Арно Толмейер
источник
-1

Я столкнулся с тем же сообщением об ошибке, чтобы вставить запись в таблицу, имеющую отношение « многие ко многим» . Моя схема базы данных была:

Student (Id , Name)
Course (Code , Title),
Student-Course (Student_ID, Course_Code)

Таблица Student и Course имеют первичные ключи Id и Code соответственно , а таблица Student-Course имеет два внешних ключа, сопоставленных с таблицами Student и Course.

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

Мое определение sql для Student-Course было:

CREATE TABLE [dbo].[Student-Course] (
    [StudentID]  VARCHAR (12) NOT NULL,
    [CourseCode] VARCHAR (10) NOT NULL,
    CONSTRAINT [FK_Student-Course_ToCourse] FOREIGN KEY ([Course_Code]) REFERENCES [dbo].[Course] ([Code]) ON DELETE CASCADE,
    CONSTRAINT [FK_Student-Course_ToStudent] FOREIGN KEY ([Student_ID]) REFERENCES [dbo].[Student] ([Id]) ON DELETE CASCADE
);

Я сделал пару внешних ключей первичным ключом этой таблицы и обновил до:

CREATE TABLE [dbo].[Student-Course] (
    [StudentID]  VARCHAR (12) NOT NULL,
    [CourseCode] VARCHAR (10) NOT NULL,
    CONSTRAINT [PK_Student-Course] PRIMARY KEY CLUSTERED ([Student_ID] ASC, [Course_Code] ASC),
    CONSTRAINT [FK_Student-Course_ToCourse] FOREIGN KEY ([Course_Code]) REFERENCES [dbo].[Course] ([Code]) ON DELETE CASCADE,
    CONSTRAINT [FK_Student-Course_ToStudent] FOREIGN KEY ([Student_ID]) REFERENCES [dbo].[Student] ([Id]) ON DELETE CASCADE
);

Надеюсь, что это решит проблемы для некоторых парней.

Саммар Раджа
источник
На этот вопрос уже слишком много ответов. Более того, почти в каждом ответе говорится «добавить первичный ключ», и каждый делает это в контексте «многие ко многим».
Герт Арнольд
Вы правы, но некоторые люди добавляют дополнительный идентификатор первичного ключа в третью таблицу, что не является хорошим подходом.
Summar Raja
Как говорит другой ответ.
Герт Арнольд