Entity Framework: таблица без первичного ключа

165

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

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

The table/view TABLE_NAME does not have a primary key defined 
and no valid primary key could be inferred. This table/view has 
been excluded. To use the entity, you will need to review your schema, 
add the correct keys, and uncomment it.

Если я хочу использовать их и изменять данные, я должен обязательно добавить PK к этим таблицам или есть обходной путь, чтобы мне не пришлось это делать?

Cris
источник
21
Процитирую Джо Селко: если у него нет первичного ключа, это не таблица . Зачем кому-то создавать «обычную» таблицу без первичного ключа? Просто добавьте эти ПК! Они понадобятся вам - скорее, чем позже ...
marc_s
4
Если это вид, это хава, посмотрите этот случай stackoverflow.com/a/10302066/413032
Davut Gürbüz
50
Совершенно верно, что не каждой таблице нужен первичный ключ. Не часто полезно, но действительно. Запутывание EF - одна из веских причин, а не то, что она требует много. ;-).
Suncat2000
25
Представьте, что я не могу изменить структуру БД в моей компании, и она была создана кем-то, кто не изменит структуру таблицы, такой сценарий возможен.
Тито
4
Это именно то, где мы находимся. Мы должны работать со сторонней базой данных Oracle, у которой нет первичных ключей.
Дэвид Брауэр

Ответы:

58

Ошибка означает именно то, что говорится.

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

Не работай над этим. Исправьте вашу модель данных.

РЕДАКТИРОВАТЬ: Я видел, что ряд людей отрицательно относятся к этому вопросу. Это нормально, я полагаю, но имейте в виду, что OP спросил о сопоставлении таблицы без первичного ключа, а не представления . Ответ все тот же. Обходить EF необходимостью иметь PK на таблицах - плохая идея с точки зрения управляемости, целостности данных и производительности.

Некоторые отмечают, что у них нет возможности исправить базовую модель данных, поскольку они сопоставляются со сторонним приложением. Это не очень хорошая идея, так как модель может измениться из-под вас. Возможно, в этом случае вы захотите сопоставить с представлением, что, опять же, не то, что спросил ОП.

Дейв Маркл
источник
44
Согласитесь в обычных senarios, но в редких senarios, таких как таблица LOG, вам просто нужно вставить записи как можно скорее. Наличие PK может быть проблемой при проверке уникальности и индексации. Кроме того, если ваш PK является IDENTITY, то возвращение сгенерированного значения в EF является другой проблемой. вместо этого использовать GUID? время генерации и индексация / сортировка - это еще одна проблема! ... ТАК В некоторых критических сенарио OLTP (таких как ведение журнала) отсутствие PK - это точка, а наличие - нет положительного!
Махмуд Моравей
5
@MahmoudMoravej: Во-первых, не путайте идеи кластеризации индексов и первичных ключей. Это не одно и то же. У вас могут быть очень производительные вставки в таблицы с кластеризованными указателями на столбцы IDENTITY. Если у вас возникнут проблемы с обслуживанием индекса, вам следует правильно разбить таблицу. Выход из таблицы без кластеризованного индекса также означает, что вы не можете эффективно дефрагментировать ее, чтобы освободить место после удалений. Мне жаль бедного человека, который пытается запросить вашу таблицу регистрации, если у нее нет индексов.
Дэйв Маркл
149
«Исправить модель данных» не является реальным ответом. Иногда нам приходится жить с неидеальными идеями, которые мы не создавали и не можем изменить. И, как сказал @Colin, есть способ сделать именно то, о чем спрашивал ОП.
TheSmurf
13
Изменение кода для удовлетворения EF само по себе является обходным решением. Не все таблицы нуждаются в первичном ключе, и их не следует заставлять. Например, у вас есть тема и она имеет 0 или много ключевых слов. Таблица ключевых слов может иметь идентификатор родительской темы и соответствующее ключевое слово. Сказать, что мне нужно переделать мою БД, потому что EF заставляет меня хромать.
Mrchief
14
Это должно быть опущено, потому что это не отвечает на вопрос. Нам часто приходится работать со сторонними базами данных, которые нельзя изменить.
Куррен
104

Я думаю, что это решено Тиллито:

Entity Framework и SQL Server View

Я процитирую его запись ниже:

У нас была та же проблема, и это решение:

Чтобы заставить платформу сущностей использовать столбец в качестве первичного ключа, используйте ISNULL.

Чтобы заставить каркас сущностей не использовать столбец в качестве первичного ключа, используйте NULLIF.

Самый простой способ применить это - заключить оператор выбора вашего представления в другой выбор.

Пример:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp

ответил 26.04.10 в 17:00 Тиллито

Colin
источник
6
+1 Это правильный ответ, в идеальном мире было бы здорово пойти и изменить все устаревшие базы данных, чтобы иметь ссылочную целостность, но в действительности это не всегда возможно.
Дилан Хейс
9
Я бы не рекомендовал это. Особенно ISNULL часть. Если EF обнаруживает два PK одинаково, он может не отображать уникальные записи и вместо этого возвращать общий объект. Это случилось со мной раньше.
Тодд
@ Тодд - как это могло произойти, если MyPrimaryID является столбцом NOT NULL?
JoeCool
@JoeCool, просто потому, что он НЕ НЕДЕЙСТВИТЕЛЕН, не означает, что он уникален. Я проголосовал «ЭТО РЕШЕНИЕ РАБОТАЕТ ...», потому что, независимо от того, в каком контексте оно используется, вы можете быть уверены в уникальности. Хотя сейчас и думаем об этом, если запись будет удалена, это фактически изменит «PK» следующих записей.
Тодд
1
-1, потому что это не дает ответа о том, как настроить код Entity Framework / C #, чтобы иметь дело с тем, как сопоставить таблицу, в которой отсутствует начальное число идентификаторов. Некоторое программное обеспечение сторонних производителей (по некоторым причинам) написано таким образом.
Эндрю Грей
27

Если я хочу использовать их и изменять данные, я должен обязательно добавить PK к этим таблицам или есть обходной путь, чтобы мне не пришлось это делать?

Для тех, кто отвечает на этот вопрос и использует Entity Framework Core, вам больше не нужно обязательно добавлять PK в эти таблицы или делать обходные пути. Начиная с EF Core 2.1 у нас появилась новая функция Query Types

Типы запросов должны использоваться для:

  • Служит типом возврата для специальных запросов FromSql ().
  • Сопоставление с представлениями базы данных.
  • Сопоставление с таблицами, для которых не определен первичный ключ.
  • Сопоставление с запросами, определенными в модели.

Так что в вашем DbContext просто добавьте следующее свойство типа DbQuery<T>вместо того, DbSet<T>как показано ниже. Предполагая, что ваше имя таблицы MyTable:

public DbQuery<MyTable> MyTables { get; set; }
CodeNotFound
источник
1
Лучший ответ, если вы используете EF Core!
Джей
17

Составные ключи также можно сделать с помощью Entity Framework Fluent API

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}
Адриан Гарсия
источник
В этом случае «ручного отображения» я обнаружил, что указание пользовательского ключа, как вы показываете, является эффективным; Кроме того, если вы не пользуетесь составным ключом (как показано в этом ответе), вы можете пометить modelBuilder.Entity<T>()цепной вызов .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)для тех необычных или составных ключей, которые нельзя полагаться быть уникальным (как правило, в любом случае.)
Эндрю Грей
5

В моем случае мне пришлось сопоставить сущность с представлением, у которого не было первичного ключа. Более того, мне не разрешили изменить это представление. К счастью, в этом представлении был столбец, представляющий собой уникальную строку. Моим решением было пометить этот столбец как первичный ключ:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[StringLength(255)]
public string UserSID { get; set; }

Обман EF. Работало отлично, никто не заметил ... :)

Борис Липшиц
источник
нет .. будет создан UserIDстолбец, если вы используете подход Code First ..!
Дипак Шарма
1
Вы не обманули EF. Вы только что приказали отключить Itentityфункцию. По сути, если я прав, он по-прежнему будет создавать UserIDдля вас столбец как PK, но он не будет автоматически увеличивать время UserIDсоздания новой записи, как это было бы по умолчанию. Кроме того, вам все еще нужно хранить различные значения в UserID.
Селдор
1
@Celdor UserSID - это строка, которая никогда не будет «автоматически увеличена». Если бы это был столбец целочисленных идентификаторов, то база данных увеличивала бы его при вставке, а не в Entity Framework.
Reggaeguitar
4

EF не требует первичного ключа в базе данных. Если это так, вы не сможете привязать сущности к представлениям.

Вы можете изменить SSDL (и CSDL), указав уникальное поле в качестве первичного ключа. Если у вас нет уникального поля, то я думаю, что вы попали. Но у вас действительно должно быть уникальное поле (и ПК), иначе вы столкнетесь с проблемами позже.

Erick

Эрик Т
источник
Это позволяет избежать взлома ISNULL. Но в зависимости от ситуации могут потребоваться другие ответы - у меня есть ощущение, что некоторые типы данных не поддерживаются для PK в EF, например.
Тодд
3

Временами бессмысленно иметь бесполезный ключ идентификации. Я нахожу, если идентификатор не используется, зачем его добавлять? Однако Entity не так уж проста, поэтому лучше всего добавить поле идентификатора. Даже в том случае, если он не используется, это лучше, чем иметь дело с непрекращающимися ошибками Entity по поводу отсутствующего идентификационного ключа.

IyaTaisho
источник
3

ЭТО РЕШЕНИЕ РАБОТАЕТ

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

Для этого вы можете добавить номер строки в ваше представление с помощью функции isNull, как показано ниже

select 
    ISNULL(ROW_NUMBER() OVER (ORDER BY xxx), - 9999) AS id
from a

ISNULL(id, number) является ключевым моментом здесь, потому что он говорит EF, что этот столбец может быть первичным ключом

Barny
источник
2
Я не предложил бы часть ISNULL все же. Если EF обнаружит два PK одинаково, он может не отобразить уникальную запись и вместо этого вернуть общий объект. Это случилось со мной раньше.
Тодд
1
Вы должны использовать isnull, иначе EF не поверит, что он не обнуляем.
Archlight
2

Приведенные выше ответы верны, если у вас действительно нет ПК.

Но если он есть, но он просто не указан с индексом в БД, и вы не можете изменить БД (да, я работаю в мире Дилберта), вы можете вручную сопоставить поле (поля), чтобы оно стало ключевым.

Покерный Злодей
источник
2

Это всего лишь дополнение к ответу @Erick T. Если нет одного столбца с уникальными значениями, обходной путь должен использовать составной ключ, как показано ниже:

[Key]
[Column("LAST_NAME", Order = 1)]
public string LastName { get; set; }

[Key]
[Column("FIRST_NAME", Order = 2)]
public string FirstName { get; set; }

Опять же, это просто обходной путь. Реальное решение состоит в том, чтобы исправить модель данных.

разработчик
источник
2

Это может быть поздно, чтобы ответить ... однако ...

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

Скажем, ваша таблица 1. Записи уникальны: уникальность создается одним столбцом внешнего ключа: 2. Записи уникальны: уникальность создается комбинацией нескольких столбцов. 3. Записи не являются уникальными (по большей части *).

Для сценариев № 1 и № 2 вы можете добавить следующую строку в метод OnModelCreating модуля DbContext: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // столько столбцов, сколько нужно, чтобы сделать записи уникальными.

Для сценария № 3 вы все равно можете использовать вышеуказанное решение (# 1 + # 2) после изучения таблицы (* что делает все записи уникальными в любом случае). Если вам необходимо включить ВСЕ столбцы, чтобы сделать все записи уникальными, вы можете добавить в таблицу столбец первичного ключа. Если эта таблица принадлежит стороннему поставщику, то клонируйте эту таблицу в вашу локальную базу данных (в одночасье или столько времени, сколько вам нужно) с произвольным добавлением столбца первичного ключа в сценарии клонирования.

Сэм Саарян
источник
1
  1. Измените структуру таблицы и добавьте основной столбец. Обновить модель
  2. Измените файл .EDMX в редакторе XML и попробуйте добавить новый столбец под тегом для этой конкретной таблицы (НЕ РАБОТАЕТ)
  3. Вместо создания новой таблицы «Первичный столбец для выхода» я создам составной ключ, задействовав все существующие столбцы ( РАБОТАЕТ )

Entity Framework: Добавление DataTable без первичного ключа к модели Entity.

Pratap
источник
Я попробовал подход с комбинированным ключом с EF 4.0, и он не работал.
Ральф Уиллгосс
Этот подход отлично сработал для меня, может быть боль работать с устаревшими системами «иногда» ...
pinmonkeyiii
1

Обновление до ответа @CodeNotFound .

В EF Core 3.0 DbQuery<T>устарела, вместо этого вы должны использовать типы сущностей Keyless, которые предположительно делают то же самое. Они настраиваются с помощью HasNoKey()метода ModelBuilder . В вашем классе DbContext, сделайте это

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<YourEntityType>(eb =>
        {
            eb.HasNoKey();
        });

}

Хотя есть ограничения, а именно:

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

Это означает, что для вопроса

Если я хочу использовать их и изменять данные, я должен обязательно добавить PK к этим таблицам или есть обходной путь, чтобы мне не пришлось это делать?

Вы не можете изменять данные таким способом - однако вы можете читать. Можно было бы предположить использование другого способа (например, ADO.NET, Dapper) для изменения данных - это может быть решением в тех случаях, когда вам редко требуется выполнять операции, не связанные с чтением, и вы все равно хотели бы придерживаться EF Core в большинстве случаев.

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

Питер Линдстен
источник
0

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

Без какого-либо уникального ключа в таблице могут (и будут) появляться дублированные записи, что очень проблематично как для уровней ORM, так и для базового понимания данных. Таблица с дублирующимися записями, вероятно, является признаком плохого дизайна.

По крайней мере, в таблице должен быть хотя бы столбец идентификаторов. Добавление столбца автоматического создания идентификатора занимает около 2 минут в SQL Server и 5 минут в Oracle. Для этого дополнительного усилия потребуется избежать множества проблем.

Ash8087
источник
Мое приложение находится в хранилище данных (с Oracle), и вы убедили меня пройти через 5 минут, чтобы добавить индекс. На самом деле это займет всего 5 минут (или немного больше, если вам нужно посмотреть или изменить ETL).
Трент
0

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

Таким образом, чтобы процитировать ответ, данный Пратапом Редди, он отлично сработал для нас.

Azerax
источник
0

Я очень рад, что моя проблема решена.

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

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

Теперь без изменения БД TI можно вставить в таблицу, в которой нет PK

спасибо всем и спасибо Pharylon

асеман арабсорхи
источник
-8

В таблице просто должен быть один столбец, который не допускает пустых значений

mister9
источник