Entity Framework: tabela bez klucza podstawowego

165

Mam istniejącą bazę danych, za pomocą której chciałbym zbudować nową aplikację przy użyciu EF4.0

Niektóre tabele nie mają zdefiniowanych kluczy podstawowych, więc kiedy tworzę nowy Entity Data Model, otrzymuję następujący komunikat:

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.

Jeśli chcę ich używać i modyfikować dane, czy koniecznie muszę dodać PK do tych tabel, czy też istnieje obejście, aby nie musieć tego robić?

Kryzys
źródło
21
Cytując Joe Celko: jeśli nie ma klucza podstawowego, nie jest tabelą . Po co, u licha, ktoś miałby tworzyć „zwykłą” tabelę bez klucza podstawowego? Po prostu dodaj te PK! Będziesz ich potrzebował - raczej wcześniej niż później ...
marc_s
4
Jeśli to widok, to trzeba spojrzeć na tę sprawę stackoverflow.com/a/10302066/413032
Davut Gürbüz
50
Jest całkowicie słuszne, że nie każda tabela potrzebuje klucza podstawowego. Niezbyt przydatne, ale ważne. Mylące EF to jeden dobry powód, nie dlatego, że zajmuje dużo czasu. ;-).
Suncat2000
25
Wyobraź sobie, że nie mogę zmodyfikować struktury bazy danych w mojej firmie i została utworzona przez kogoś, kto nie zmieni struktury tabeli, taki scenariusz jest możliwy.
Tito
4
Dokładnie w tym miejscu jesteśmy. Musimy pracować z zewnętrzną bazą danych Oracle, która nie ma kluczy podstawowych.
David Brower

Odpowiedzi:

59

Błąd oznacza dokładnie to, co mówi.

Nawet gdybyś mógł to obejść, zaufaj mi, nie chcesz. Liczba mylących błędów, które można wprowadzić, jest oszałamiająca i przerażająca, nie wspominając o tym, że Twoja wydajność prawdopodobnie spadnie w dół.

Nie omijaj tego. Napraw model danych.

EDYCJA: Widziałem, że wiele osób odrzuca to pytanie. Przypuszczam, że to w porządku, ale pamiętaj, że OP zapytał o mapowanie tabeli bez klucza podstawowego, a nie widoku . Odpowiedź jest wciąż taka sama. Obchodzenie się z potrzebą posiadania PK na tabelach jest złym pomysłem z punktu widzenia możliwości zarządzania, integralności danych i wydajności.

Niektórzy zauważyli, że nie mają możliwości naprawienia bazowego modelu danych, ponieważ mapują do aplikacji innej firmy. To nie jest dobry pomysł, ponieważ model może się zmienić spod ciebie. Prawdopodobnie w takim przypadku chciałbyś zmapować do widoku, który znowu nie jest tym, o co prosił PO.

Dave Markle
źródło
44
Zgadzam się w zwykłych senariach, ale w rzadkich senariach, takich jak tabela LOG, wystarczy wstawić rekordy jak najszybciej. Posiadanie PK może być problemem podczas sprawdzania unikalności i indeksowania. Również jeśli twój PK to TOŻSAMOŚĆ, więc zwrócenie wygenerowanej wartości do EF jest innym problemem. używasz zamiast tego GUID? czas generowania i indeksowanie / sortowanie to kolejna kwestia! ... TAK W niektórych krytycznych senariach OLTP (takich jak rejestrowanie) brak PK jest punktem i nie ma żadnego pozytywnego punktu!
Mahmoud Moravej
5
@MahmoudMoravej: Po pierwsze, nie mieszaj pomysłów dotyczących grupowania indeksów i kluczy podstawowych. To nie to samo. Możesz mieć bardzo wydajne wstawki w tabelach ze zgrupowanymi wskazaniami w kolumnach IDENTITY. Jeśli napotkasz problemy z obsługą indeksu, powinieneś odpowiednio podzielić tabelę na partycje. Pozostawienie tabeli bez indeksu klastrowego oznacza również, że nie można jej skutecznie zdefragmentować w celu odzyskania miejsca po usunięciu. Szkoda biednej osoby, która próbuje przeszukać twoją tabelę logowania, jeśli nie ma ona indeksów.
Dave Markle
149
„Napraw model danych” nie jest właściwą odpowiedzią. Czasami musimy żyć w mniej niż idealnych sytuacjach, których nie stworzyliśmy i nie możemy zmienić. I, jak powiedział @Colin, JEST sposób, aby zrobić dokładnie to, o co prosił OP.
TheSmurf
13
Zmiana kodu w celu spełnienia wymagań EF jest obejściem samym w sobie. Nie wszystkie tabele wymagają klucza podstawowego ani nie należy ich do tego zmuszać. Np. Masz temat i zawiera 0 lub wiele słów kluczowych. Tabela słów kluczowych może mieć identyfikator tematu nadrzędnego i odpowiadające mu słowo kluczowe. Powiedzieć, że muszę przebudować mój DB, ponieważ EF zmusza mnie do tego, jest kiepski.
Mrchief,
14
Należy to zlekceważyć, ponieważ nie odpowiada na pytanie. Często musimy pracować z bazami danych stron trzecich, których nie można zmienić.
Kurren
104

Myślę, że rozwiązuje to Tillito:

Entity Framework i widok SQL Server

Zacytuję jego wpis poniżej:

Mieliśmy ten sam problem i oto rozwiązanie:

Aby zmusić strukturę jednostki do używania kolumny jako klucza podstawowego, użyj ISNULL.

Aby wymusić na platformie jednostki, aby nie używała kolumny jako klucza podstawowego, użyj wartości NULLIF.

Łatwym sposobem na zastosowanie tego jest zawinięcie wyrażenia select swojego widoku w innym select.

Przykład:

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

odpowiedział 26 kwietnia 2010 o 17:00 przez Tillito

Colin
źródło
6
+1 To jest właściwa odpowiedź. W idealnym świecie byłoby wspaniale wejść i zmodyfikować wszystkie starsze bazy danych, aby uzyskać referencyjną integralność, ale w rzeczywistości nie zawsze jest to możliwe.
Dylan Hayes
9
Nie polecałbym tego. Szczególnie część ISNULL. Jeśli EF wykryje dwa takie same PK, może nie renderować unikatowych rekordów i zamiast tego zwrócić obiekt współdzielony. Przydarzyło mi się to już wcześniej.
Todd,
@Todd - jak to się mogło stać, jeśli MyPrimaryID jest kolumną NOT NULL?
JoeCool
@JoeCool, tylko dlatego, że NIE jest NULL, nie oznacza, że ​​JEST wyjątkowy. Głosowałem za „TO ROZWIĄZANIE DZIAŁA…”, ponieważ niezależnie od kontekstu, w jakim jest używane, możesz być pewny wyjątkowości. Chociaż myślę o tym teraz, jeśli rekord zostanie usunięty, skutecznie zmieni to kolejne rekordy „PK”.
Todd,
1
-1, ponieważ nie odpowiada to, jak skonfigurować kod Entity Framework / C #, aby poradzić sobie z mapowaniem do tabeli, która nie ma materiału siewnego tożsamości. Niektóre programy innych firm są (z jakiegoś powodu) napisane w ten sposób.
Andrew Gray
27

Jeśli chcę ich używać i modyfikować dane, czy koniecznie muszę dodać PK do tych tabel, czy też istnieje obejście, aby nie musieć tego robić?

Osoby, które dotarły do ​​tego pytania i korzystają z Entity Framework Core, nie muszą już koniecznie dodawać PK do tych tabel ani wykonywać żadnego obejścia. Od EF Core 2,1 mamy nowe typy zapytań funkcji

Typy zapytań muszą być używane dla:

  • Służy jako typ zwracany dla zapytań ad hoc FromSql ().
  • Mapowanie do widoków bazy danych.
  • Mapowanie do tabel, które nie mają zdefiniowanego klucza podstawowego.
  • Mapowanie do zapytań zdefiniowanych w modelu.

Więc w swoim DbContext po prostu dodaj następującą właściwość type DbQuery<T>zamiast DbSet<T>jak poniżej. Zakładając, że nazwa twojej tabeli to MyTable:

public DbQuery<MyTable> MyTables { get; set; }
CodeNotFound
źródło
1
Najlepsza odpowiedź, jeśli używasz EF Core!
Jay
17

Klucze złożone można również wykonać za pomocą interfejsu 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 });
        ...
     }
}
Adrian Garcia
źródło
W tego rodzaju przypadku „ręcznego mapowania” stwierdziłem, że określenie niestandardowego klucza, tak jak pokazujesz, jest skuteczne; dodatkowo, jeśli nie masz korzyści z klucza złożonego (jak pokazano w tej odpowiedzi), możesz oznaczyć modelBuilder.Entity<T>()wywołanie łańcuchowe .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)za pomocą tych och tak specjalnych kluczy, które nie są naturalne ani złożone, ale można na nich polegać był wyjątkowy (tak czy inaczej)
Andrew Gray
5

W moim przypadku musiałem zmapować jednostkę do widoku, który nie miał klucza podstawowego. Ponadto nie wolno mi było modyfikować tego widoku. Na szczęście ten widok miał kolumnę, która była unikalnym ciągiem. Moim rozwiązaniem było oznaczenie tej kolumny jako klucza podstawowego:

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

Oszukany EF. Działało idealnie, nikt nie zauważył ... :)

Boris Lipschitz
źródło
nie .. Utworzy UserIDkolumnę, jeśli używasz podejścia Code First ..!
Deepak Sharma
1
Nie oszukałeś EF. Właśnie nakazałeś wyłączenie Itentityfunkcji. Zasadniczo, jeśli mam rację, nadal utworzy UserIDdla ciebie kolumnę jako PK, ale nie zwiększy automatycznie UserIDpodczas tworzenia nowego rekordu, jak byłoby to domyślnie. Ponadto nadal musisz zachować różne wartości w UserID.
Celdor
1
@Celdor UserSID to ciąg znaków, który nigdy nie zostałby „automatycznie zwiększony”. Jeśli byłaby to kolumna z tożsamością całkowitą, baza danych zwiększyłaby ją podczas wstawiania, a nie Entity Framework.
reggaeguitar
4

EF nie wymaga klucza podstawowego w bazie danych. Gdyby tak było, nie można by powiązać jednostek z widokami.

Możesz zmodyfikować SSDL (i CSDL), aby określić unikalne pole jako klucz podstawowy. Jeśli nie masz unikalnego pola, uważam, że jesteś wężem. Ale naprawdę powinieneś mieć unikalne pole (i PK), w przeciwnym razie napotkasz później problemy.

Erick

Erick T.
źródło
Pozwala to uniknąć włamania do ISNULL. Ale w zależności od sytuacji mogą być wymagane inne odpowiedzi - mam wrażenie, że niektóre typy danych nie są obsługiwane na przykład dla PK w EF.
Todd,
3

Posiadanie bezużytecznego klucza tożsamości jest czasami bezcelowe. Znajduję, jeśli identyfikator nie jest używany, po co go dodawać? Jednak Entity nie wybacza w tej kwestii, więc najlepiej byłoby dodać pole ID. Nawet w przypadku, gdy nie jest używany, jest to lepsze niż radzenie sobie z nieustannymi błędami Entity dotyczącymi brakującego klucza tożsamości.

IyaTaisho
źródło
3

TO ROZWIĄZANIE DZIAŁA

Nie musisz mapować ręcznie, nawet jeśli nie masz PK. Wystarczy poinformować EF, że jedna z kolumn jest indeksowana, a kolumna indeksu nie dopuszcza wartości null.

Aby to zrobić, możesz dodać numer wiersza do swojego widoku za pomocą funkcji isNull, jak poniżej

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

ISNULL(id, number) jest tutaj kluczowym punktem, ponieważ informuje EF, że ta kolumna może być kluczem podstawowym

Barny
źródło
2
Nie sugerowałbym jednak części ISNULL. Jeśli EF wykryje dwa PK takie same, może nie renderować unikatowego rekordu i zamiast tego zwrócić obiekt współdzielony. Przydarzyło mi się to już wcześniej.
Todd,
1
Musisz użyć isnull, w przeciwnym razie EF nie uwierzy, że nie ma wartości null.
Archlight,
2

Powyższe odpowiedzi są poprawne, jeśli naprawdę nie masz PK.

Ale jeśli istnieje, ale po prostu nie jest określony indeksem w bazie danych, i nie możesz zmienić bazy danych (tak, pracuję w świecie Dilberta), możesz ręcznie zmapować pole (pola) jako klucz.

Pokerowy czarny charakter
źródło
2

To tylko dodatek do odpowiedzi @Erick T. Jeśli nie ma jednej kolumny z unikatowymi wartościami, obejściem jest użycie klucza złożonego w następujący sposób:

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

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

Ponownie, to tylko obejście. Prawdziwym rozwiązaniem jest naprawienie modelu danych.

Deweloper
źródło
2

To może za późno na odpowiedź ... jednak ...

Jeśli tabela nie ma klucza podstawowego, istnieje kilka scenariuszy, które należy przeanalizować, aby EF działał poprawnie. Zasada jest taka: EF będzie działał z tabelami / klasami z kluczem podstawowym. W ten sposób śledzi ...

Powiedzmy, twoja tabela 1. Rekordy są niepowtarzalne: niepowtarzalność jest określana przez pojedynczą kolumnę klucza obcego: 2. Rekordy są niepowtarzalne: niepowtarzalność tworzy kombinacja wielu kolumn. 3. Rekordy nie są unikalne (w większości *).

W scenariuszach nr 1 i 2 można dodać następujący wiersz do metody OnModelCreating modułu DbContext: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // tyle kolumn, ile potrzeba, aby rekordy były unikalne.

W scenariuszu # 3 możesz nadal korzystać z powyższego rozwiązania (# 1 + # 2) po przestudiowaniu tabeli (* co i tak sprawia, że ​​wszystkie rekordy są unikalne). Jeśli musisz uwzględnić WSZYSTKIE kolumny, aby wszystkie rekordy były unikalne, możesz dodać kolumnę klucza podstawowego do swojej tabeli. Jeśli ta tabela pochodzi od zewnętrznego dostawcy, sklonuj ją do lokalnej bazy danych (przez noc lub tyle razy, ile potrzebujesz) z kolumną klucza podstawowego dodaną dowolnie za pośrednictwem skryptu klonowania.

Sam Saarian
źródło
1
  1. Zmień strukturę tabeli i dodaj kolumnę podstawową. Zaktualizuj model
  2. Zmodyfikuj plik .EDMX w edytorze XML i spróbuj dodać nową kolumnę pod znacznikiem dla tej konkretnej tabeli (NIE DZIAŁA)
  3. Zamiast tworzyć nową kolumnę podstawową do tabeli wyjściowej, utworzę klucz złożony, obejmując wszystkie istniejące kolumny ( PRACOWANE )

Entity Framework: dodawanie DataTable bez klucza podstawowego do modelu jednostki.

Pratap
źródło
Wypróbowałem podejście złożone z klucza w EF 4.0 i nie zadziałało.
Ralph Willgoss
To podejście działało idealnie dla mnie, może być uciążliwe przy pracy ze starszymi systemami "czasami" ...
pinmonkeyiii
1

Zaktualizuj do odpowiedzi @CodeNotFound .

W EF Core 3,0 DbQuery<T>został przestarzały, zamiast tego należy używać typów jednostek bez kluczy, które rzekomo robią to samo. Są one konfigurowane za pomocą HasNoKey()metody ModelBuilder . Zrób to w swojej klasie DbContext

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

}

Istnieją jednak ograniczenia, w szczególności:

  • Nigdy nie są śledzone pod kątem zmian w DbContext i dlatego nigdy nie są wstawiane, aktualizowane ani usuwane z bazy danych.
  • Obsługuje tylko podzbiór funkcji mapowania nawigacji, w szczególności:
    • Mogą nigdy nie odgrywać roli głównego końca związku.
    • Mogą nie mieć nawigacji do posiadanych podmiotów
    • Mogą zawierać tylko właściwości nawigacji referencyjnej wskazujące na zwykłe jednostki.
    • Jednostki nie mogą zawierać właściwości nawigacji do typów jednostek bez kluczy.

Oznacza to, że na pytanie

Jeśli chcę ich używać i modyfikować dane, czy koniecznie muszę dodać PK do tych tabel, czy też istnieje obejście, aby nie musieć tego robić?

Nie możesz modyfikować danych w ten sposób - możesz jednak czytać. Można sobie jednak wyobrazić użycie innego sposobu (np. ADO.NET, Dapper) do modyfikowania danych - może to być rozwiązanie w przypadkach, gdy rzadko trzeba wykonywać operacje bez odczytu i nadal chciałbyś trzymać się EF Core w większości przypadków.

Ponadto, jeśli naprawdę potrzebujesz / chcesz pracować z tabelami sterty (bezkluczowymi) - rozważ porzucenie EF i użyj innego sposobu, aby rozmawiać z bazą danych.

Peter Lindsten
źródło
0

Z praktycznego punktu widzenia każdy stół - nawet zdenormalizowany, taki jak stół magazynowy - powinien mieć klucz podstawowy. Lub, w przeciwnym razie, powinien mieć przynajmniej unikalny indeks nie dopuszczający wartości null.

Bez jakiegoś unikalnego klucza zduplikowane rekordy mogą (i będą) pojawiać się w tabeli, co jest bardzo problematyczne zarówno dla warstw ORM, jak i dla podstawowego zrozumienia danych. Tabela, która zawiera zduplikowane rekordy, jest prawdopodobnie objawem złego projektu.

Przynajmniej tabela powinna mieć przynajmniej kolumnę tożsamości. Dodanie automatycznie generowanej kolumny ID trwa około 2 minut w SQL Server i 5 minut w Oracle. Za ten dodatkowy wysiłek można uniknąć wielu, wielu problemów.

Ash8087
źródło
Moja aplikacja znajduje się w hurtowni danych (w Oracle) i przekonałeś mnie, żebym przez 5 minut dodała indeks. Naprawdę zajmuje to tylko 5 minut (lub nieco więcej, jeśli chcesz to sprawdzić lub zmodyfikować ETL).
Trent
0

Napotkaliśmy również ten problem i chociaż mieliśmy kolumnę zawierającą wartości null, ważne było to, że mieliśmy kolumnę zależną, która nie zawierała wartości null, i że połączenie tych dwóch kolumn było unikalne.

Cytując odpowiedź udzieloną przez Pratapa Reddy'ego, zadziałało to dobrze dla nas.

Azerax
źródło
-8

Tabela musi mieć tylko jedną kolumnę, która nie zezwala na wartości null

mister9
źródło