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


165

У мене є існуюча БД, з якою я хотів би створити новий додаток, використовуючи EF4.0

У деяких таблицях не визначені первинні ключі, так що коли я створюю нову модель даних Entity, я отримую таке повідомлення:

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.

Якщо я хочу використовувати їх і змінювати дані, чи потрібно обов’язково додати ПК до цих таблиць, чи є рішення, щоб мені не довелося?


21
Цитую Джо Селко: якщо він не має первинного ключа, це не таблиця . Чому б на землі хтось створив "звичайну" таблицю без первинного ключа ?? Просто додайте ці ПК! Вони вам знадобляться - швидше, ніж пізніше ....
marc_s

4
Якщо його погляд, це виглядає ця справа stackoverflow.com/a/10302066/413032
Davut Gürbüz

50
Цілком справедливо, що не кожна таблиця потребує первинного ключа. Не часто корисний, але дійсний. Заплутаність EF - одна вагома причина, а не те, що потрібно багато. ;-).
Suncat2000

25
Уявіть, що я не можу змінити структуру БД у своїй компанії, і її створив хтось, хто не зможе змінити структуру таблиці, такий сценарій можливий.
Тіто

4
Саме тут ми знаходимось. Ми повинні працювати з сторонніми базами даних Oracle, які не мають первинних ключів.
Девід Броуер

Відповіді:


58

Помилка означає саме те, що вона говорить.

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

Не обійтися цим. Зафіксуйте модель даних.

EDIT: Я бачив, що низка людей відмовляються від цього питання. Я гадаю, гадаю, але майте на увазі, що ОП запитала про відображення таблиці без первинного ключа, а не перегляду . Відповідь все одно. Заохочення потреби EF у створенні ПК на таблицях є поганою ідеєю з точки зору керованості, цілісності даних та продуктивності.

Деякі зауважували, що вони не мають можливості виправити основні моделі даних, оскільки вони відображаються на сторонніх програмах. Це не дуже гарна ідея, оскільки модель може змінитися з-під вас. Можливо, у такому випадку ви хочете відобразити вигляд, який, знову ж таки, не є тим, що просив ОП.


44
Погодьтеся в загальних сенаріонах, але в таких рідкісних сенаріях, як таблиця LOG, вам потрібно просто вставити записи як можна швидше Наявність ПК може бути проблемою при перевірці унікальності та індексації. Також якщо ваш ПК - це ІДЕНТИЧНІСТЬ, то повернення згенерованого значення до EF - це ще одна проблема. використовуючи GUID замість цього? час генерації та індексація / сортування - це ще одна проблема! ... ТАК У деяких критичних сенаріях OLTP (наприклад, Ведення журналів), які не мають ПК, це точка, і у неї немає жодної позитивної точки!
Махмуд Моравей

5
@MahmoudMoravej: По-перше, не змішуйте ідеї кластеризації індексів та первинних ключів. Вони не те саме. Ви можете мати дуже високоефективні вставки в таблиці з кластеризованими індексами на стовпцях IDENTITY. Якщо у вас виникли проблеми з технічним обслуговуванням індексу, слід правильно розподілити таблицю. Залишення таблиці без кластерного індексу також означає, що ви не можете її ефективно дефрагментувати, щоб повернути пробіл після видалення. Я шкодую бідного, який намагається запитати вашу таблицю реєстрації, якщо у неї немає індексів.
Дейв Маркл

149
"Виправити модель даних" - не справжня відповідь. Іноді нам доводиться жити з менш ідеальними ситуаціями, які ми не створили і не можемо змінити. І, як сказав @Colin, існує спосіб зробити саме те, що просив ОП.
TheSmurf

13
Зміна коду для задоволення EF - це сама по собі вирішення проблеми. Не всі таблиці потребують первинного ключа, і їх не варто змушувати. Наприклад, ви маєте тему, вона містить 0 або багато ключових слів. Таблиця ключових слів може мати батьківський ідентифікатор теми та відповідне ключове слово. Сказати, що мені потрібно переробити свою БД, оскільки EF змушує мене кульгати.
Mrchief

14
Це слід негативно сприймати, оскільки це не відповідає на запитання. Нам часто потрібно працювати з сторонніми базами даних, які неможливо змінити.
Куррен

104

Я думаю, що це вирішує Tillito:

Entity Framework та перегляд SQL Server

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

У нас була така ж проблема, і це рішення:

Щоб змусити структуру сутності використовувати стовпчик як основний ключ, використовуйте ISNULL.

Щоб змусити структуру сутності не використовувати стовпчик як основний ключ, використовуйте NULLIF.

Простий спосіб застосувати це - перенести вибраний вигляд вашого перегляду в інший вибір.

Приклад:

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

відповів 26 квітня '10 о 17:00 від Tillito


6
+1 Це правильна відповідь, в ідеальному світі було б чудово зайти і змінити всі застарілі бази даних, щоб мати референтну цілісність, але насправді це не завжди можливо.
Ділан Хейс

9
Я б не рекомендував цього. Особливо ІСНУЮЧА частина. Якщо EF виявить два ПК однакові, він може не відображати унікальну запис (и), а натомість повертає спільний об'єкт. Це сталося зі мною раніше.
Тодд

@Todd - як це могло статися, якщо MyPrimaryID - це стовпець NOT NULL?
JoeCool

@JoeCool, тому що НЕ НУЛЬНИЙ не означає, що він унікальний. Я підтримав "ЦІ РОБОТИ РОБОТИ ...", тому що незалежно від того, в якому контексті він використовується, ви можете бути впевнені в унікальності. Хоча думати про це зараз, якщо запис буде видалено, це фактично змінить наступні записи "PK".
Тодд

1
-1, тому що це не відповідає, як налаштувати Entity Framework / C #-код для вирішення того, як зіставити таблицю, у якій відсутнє насіння ідентичності. Деяке програмне забезпечення сторонніх виробників написано (чомусь) таким чином.
Ендрю Грей

27

Якщо я хочу використовувати їх і змінювати дані, чи потрібно обов’язково додати ПК до цих таблиць, чи є рішення, щоб мені не довелося?

Для тих, хто стикається з цим питанням і використовує Entity Framework Core, вам більше не потрібно обов'язково додавати ПК в таблиці таблиць або робити якісь вирішення. З EF Core 2.1 у нас є нова функція Типи запитів

Типи запитів повинні використовуватися для:

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

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

public DbQuery<MyTable> MyTables { get; set; }

1
Найкраща відповідь, якщо ви використовуєте EF Core!
Джей

17

Складові ключі також можна зробити за допомогою API Entity Framework Fluent

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стовпець для вас як ПК, але він не збільшиться автоматично, UserIDколи ви створите новий запис, як було б за замовчуванням. Крім того, ви все ще повинні зберігати різні значення в UserID.
Celdor

1
@Celdor UserSID - це рядок, він ніколи не буде "автоматично збільшуватися". Якщо це був цілий стовпець ідентичності, то база даних збільшувала б його на вставці, а не Entity Framework.
reggaeguitar

4

EF не вимагає первинного ключа в базі даних. Якщо це було так, ви не можете прив’язати об'єкти до переглядів.

Ви можете змінити SSDL (і CSDL), щоб вказати унікальне поле в якості основного ключа. Якщо у вас немає унікального поля, я вважаю, що ви шлангували. Але ви дійсно повинні мати унікальне поле (і ПК), інакше пізніше ви зіткнетеся з проблемами.

Ерік


Це дозволяє уникнути зламу ISNULL. Але залежно від ситуації можуть знадобитися інші відповіді - у мене таке відчуття, що деякі типи даних не підтримуються, наприклад, для ПК в EF.
Тодд

3

Мати марний ідентифікаційний ключ часом безглуздо. Я знаходжу, якщо ідентифікатор не використовується, навіщо його додавати? Однак Entity не настільки прощає про це, тому найкраще додавати поле ідентифікатора. Навіть у випадку, якщо він не використовується, це краще, ніж мати справу з непостійними помилками Entity щодо відсутнього ключа ідентифікації.


3

ЦЕ РІШЕННЯ РОБОТИ

Не потрібно проводити картування вручну, навіть якщо у вас немає ПК. Вам просто потрібно сказати EF, що один з ваших стовпців є індексом, а стовпець індексу не є нульовим.

Для цього ви можете додати номер рядка до перегляду за допомогою функції isNull, як описано нижче

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

ISNULL(id, number) тут є ключовим моментом, оскільки воно говорить EF, що цей стовпець може бути первинним ключем


2
Я б не пропонував ISNULL частину. Якщо EF виявить два ПК однакові, він може не надати унікальний запис, а натомість поверне спільний об'єкт. Це сталося зі мною раніше.
Тодд

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 ви можете додати наступний рядок до модуля DbContext метод OnModelCreating: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // стільки стовпців, скільки потрібно, щоб зробити записи унікальними.

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


1
  1. Змініть структуру таблиці та додайте первинний стовпець. Оновіть модель
  2. Змініть файл .EDMX у редакторі XML і спробуйте додати новий стовпець під тегом для цієї конкретної таблиці (НЕ БУДЕ РОБОТИ)
  3. Замість створення нового первинного стовпця до таблиці, що виходить, я зроблю складений ключ, залучаючи всі існуючі стовпці ( РОБОТИ )

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


Я спробував підхід із складеного ключа з 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 і тому ніколи не вставляються, не оновлюються та не видаляються з бази даних.
  • Підтримуйте лише підмножину можливостей навігаційного відображення, зокрема:
    • Вони ніколи не можуть виступати головним кінцем відносин.
    • Вони можуть не мати навігації до юридичних осіб
    • Вони можуть містити лише базові властивості навігації, що вказують на звичайні об'єкти.
    • Суб'єкти не можуть містити властивості навігації для типів ключових типів.

Це означає, що для питання про

Якщо я хочу використовувати їх і змінювати дані, чи потрібно обов’язково додати ПК до цих таблиць, чи є рішення, щоб мені не довелося?

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

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


0

З практичної точки зору, кожен стіл - навіть денормалізований стіл, як складський стіл - повинен мати первинний ключ. Або, якщо цього не зробити, він повинен принаймні мати унікальний індекс, який не зводиться до нуля.

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

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


Моя програма знаходиться в налаштуваннях сховища даних (з Oracle), і ви переконали мене пройти 5 хвилин, щоб додати індекс. Це дійсно займає лише 5 хвилин (або трохи більше, якщо вам потрібно переглянути або змінити ETL).
Трент

0

Ми також зіткнулися з цією проблемою, і, хоча у нас був стовпець, який містить нулі, важливим було те, що у нас був залежний стовпець, який не мав нулей, і що поєднання цих двох стовпців було унікальним.

Тож, щоб процитувати відповідь Пратапа Редді, у нас це спрацювало чудово.


0

Я дуже радий, що моя проблема вирішена.

Неможливо оновити EntitySet - тому що в ньому є DefiningQuery і не існує елемента <UpdateFunction>

і зробіть це: Подивіться нижче цього рядка і знайдіть тег. У ньому буде великий оператор select ol 'select. Видаліть тег і його вміст ..

тепер без зміни БД TI може вставити в таблицю, яка не має ПК

дякую всім і дякую Фарилону



-8

У таблиці просто повинен бути один стовпець, який не дозволяє нулю

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.