Entity Framework: tabella senza chiave primaria


165

Ho un DB esistente con il quale vorrei creare una nuova app usando EF4.0

Alcune tabelle non hanno le chiavi primarie definite in modo tale che quando creo un nuovo modello di dati di entità, ottengo il seguente messaggio:

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.

Se voglio usarli e modificare i dati, devo necessariamente aggiungere un PK a quelle tabelle, oppure esiste una soluzione alternativa che non è necessario?


21
Per citare Joe Celko: se non ha una chiave primaria, non è una tabella . Perché mai qualcuno dovrebbe creare una tabella "normale" senza una chiave primaria ?? Basta aggiungere quelli PK! Ne avrai bisogno - piuttosto prima che dopo ....
marc_s

4
Se la sua una corretta visualizzazione sono Hava uno sguardo questo caso stackoverflow.com/a/10302066/413032
Davut Gürbüz

50
È perfettamente valido che non tutte le tabelle richiedono una chiave primaria. Non spesso utile, ma valido. Confondere EF è una buona ragione, non che ci vuole molto. ;-).
Suncat2000,

25
Immagina di non poter modificare la struttura del DB nella mia azienda ed è stata creata da qualcuno che non cambierà la struttura della tabella, questo scenario è possibile.
Tito,

4
Questo è esattamente dove siamo. Dobbiamo lavorare con un database Oracle di terze parti che non ha chiavi primarie.
David Brower,

Risposte:


58

L'errore indica esattamente ciò che dice.

Anche se tu potessi aggirare questo, fidati di me, non vuoi. Il numero di bug confusi che potrebbero essere introdotti è sconcertante e spaventoso, per non parlare del fatto che la tua performance probabilmente scenderà nei tubi.

Non aggirare questo. Correggi il tuo modello di dati.

EDIT: Ho visto che un certo numero di persone stanno votando a fondo su questa domanda. Va bene, suppongo, ma tieni presente che l'OP ha chiesto di mappare una tabella senza una chiave primaria, non una vista . La risposta è sempre la stessa Aggirare il bisogno dell'EF di avere un PK sui tavoli è una cattiva idea dal punto di vista della gestibilità, dell'integrità dei dati e delle prestazioni.

Alcuni hanno commentato che non hanno la possibilità di correggere il modello di dati sottostante perché stanno eseguendo il mapping a un'applicazione di terze parti. Non è una buona idea, poiché il modello può cambiare da sotto di te. Probabilmente, in quel caso, si vorrebbe mappare una vista, che, di nuovo, non è ciò che l'OP ha chiesto.


44
Accetto in senari comuni ma in senari rari come la tabella LOG è sufficiente inserire i record al più presto. Avere PK può essere un problema quando si verifica l'unicità e l'indicizzazione. Anche se il tuo PK è IDENTITÀ, quindi restituire il valore generato all'EF è un altro problema. usando invece GUID? il tempo di generazione e l'indicizzazione / l'ordinamento sono un altro problema! ... SO IN alcuni senarios OLTP critici (come la registrazione) non avere PK è un punto e averlo non ha alcun punto positivo!
Mahmoud Moravej,

5
@MahmoudMoravej: Prima di tutto, non confondere le idee di indici cluster e chiavi primarie. Non sono la stessa cosa. È possibile avere inserti con prestazioni molto elevate su tabelle con indicazioni raggruppate su colonne IDENTITY. In caso di problemi con la manutenzione dell'indice, è necessario partizionare correttamente la tabella. Lasciare una tabella senza indice cluster significa anche che non è possibile deframmentarlo in modo efficace per recuperare spazio dopo le eliminazioni. Mi dispiace per la povera persona che cerca di interrogare la tua tabella di registrazione se non ha indici.
Dave Markle,

149
"Correggi il tuo modello di dati" non è una vera risposta. A volte dobbiamo vivere con situazioni tutt'altro che ideali che non abbiamo creato e che non possiamo cambiare. E, come ha detto @Colin, esiste un modo per fare esattamente ciò che l'OP stava chiedendo.
TheSmurf,

13
La modifica del codice per soddisfare EF è una soluzione in sé. Non tutte le tabelle necessitano di una chiave primaria né dovrebbero essere forzate. Ad esempio, hai un argomento e ha 0 o più parole chiave. La tabella delle parole chiave può avere un ID argomento principale e una parola chiave corrispondente. Dire che ho bisogno di rimodellare il mio DB perché EF mi obbliga a perdere tempo.
Mrchief,

14
Questo dovrebbe essere ridimensionato perché non risponde alla domanda. Spesso abbiamo bisogno di lavorare con database di terze parti che non possono essere modificati.
Kurren,

104

Penso che questo sia risolto da Tillito:

Entity Framework e SQL Server View

Citerò la sua voce di seguito:

Abbiamo avuto lo stesso problema e questa è la soluzione:

Per forzare il framework di entità a utilizzare una colonna come chiave primaria, utilizzare ISNULL.

Per forzare il framework di entità a non utilizzare una colonna come chiave primaria, utilizzare NULLIF.

Un modo semplice per applicare questo è avvolgere l'istruzione select della vista in un'altra selezione.

Esempio:

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

Tillito rispose il 26 aprile 10 alle 17:00


6
+1 Questa è la risposta giusta, in un mondo perfetto sarebbe bello entrare e modificare tutti i database legacy per avere integrità referenziale, ma in realtà non è sempre possibile.
Dylan Hayes,

9
Non lo consiglierei. Paricolarmente la parte ISNULL. Se EF rileva due PK uguali, potrebbe non eseguire il rendering dei record univoci e restituire invece un oggetto condiviso. Questo mi è già successo.
Todd,

@Todd - come potrebbe mai accadere se MyPrimaryID è una colonna NOT NULL?
JoeCool

@JoeCool, solo perché NON è NULL non significa che È unico. Ho votato "QUESTA SOLUZIONE FUNZIONA ...", perché non importa in quale contesto venga utilizzato, si può essere certi dell'unicità. Sebbene ci stia pensando ora, se viene eliminato un record che cambierà effettivamente il "PK" dei seguenti record.
Todd,

1
-1, perché questo non risponde a come configurare il codice Entity Framework / C # per gestire il mapping a una tabella in cui manca un seed identità. Alcuni software di terze parti sono (per qualche motivo) scritti in questo modo.
Andrew Gray,

27

Se voglio usarli e modificare i dati, devo necessariamente aggiungere un PK a quelle tabelle, oppure esiste una soluzione alternativa che non è necessario?

Per coloro che raggiungono questa domanda e utilizzano Entity Framework Core, non è più necessario aggiungere necessariamente un PK a queste tabelle o eseguire una soluzione alternativa. Da EF Core 2.1 abbiamo una nuova funzionalità Tipi di query

I tipi di query devono essere utilizzati per:

  • Funge da tipo di ritorno per le query FromSql () ad hoc.
  • Mappatura delle visualizzazioni del database.
  • Mappatura su tabelle per le quali non è stata definita una chiave primaria.
  • Mappatura su query definite nel modello.

Quindi nel tuo DbContext basta aggiungere la seguente proprietà di tipo DbQuery<T>anziché DbSet<T>come di seguito. Supponendo che il nome della tabella sia MyTable:

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

1
La migliore risposta se si utilizza EF Core!
Jay,

17

Le chiavi composite possono anche essere eseguite con l'API Fluent di Entity Framework

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}

In questo tipo di caso di "mappatura manuale", ho scoperto che specificare una chiave personalizzata mentre si mostra è efficace; inoltre, se non hai il vantaggio di un tasto composito (come mostrato in questa risposta) puoi taggare una modelBuilder.Entity<T>()chiamata a catena con .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)quei tasti così speciali che non sono naturali o composti, ma che possono essere fatti valere per essere unico (di solito, comunque.)
Andrew Gray,

5

Nel mio caso ho dovuto mappare un'entità a una vista, che non aveva la chiave primaria. Inoltre, non mi è stato permesso di modificare questa vista. Fortunatamente, questa vista aveva una colonna che era una stringa unica. La mia soluzione era contrassegnare questa colonna come chiave primaria:

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

EF imbrogliato. Ha funzionato perfettamente, nessuno se ne è accorto ... :)


no .. Creerà la UserIDcolonna se si utilizza l'approccio Code First ..!
Deepak Sharma,

1
Non hai imbrogliato EF. Hai appena comandato di disattivare la Itentityfunzione. Fondamentalmente, se ho ragione, creerà comunque una UserIDcolonna per te come PK, ma non aumenterà automaticamente il valore UserIDquando crei un nuovo record come sarebbe per impostazione predefinita. Inoltre, è ancora necessario mantenere valori distinti in UserID.
Celdor

1
@Celdor UserSID è una stringa, non verrebbe mai "aumentato automaticamente". Se fosse una colonna di identità intera, il database la incrementerebbe al momento dell'inserimento, non di Entity Framework.
reggaeguitar,

4

EF non richiede una chiave primaria nel database. In tal caso, non è possibile associare le entità alle viste.

È possibile modificare SSDL (e CSDL) per specificare un campo univoco come chiave primaria. Se non hai un campo unico, allora credo che tu sia afflitto. Ma dovresti davvero avere un campo unico (e un PK), altrimenti ti imbatterai in problemi in seguito.

Erick


Questo evita l'hack ISNULL. Ma a seconda della situazione, potrebbero essere necessarie altre risposte: ad esempio, ho la sensazione che alcuni tipi di dati non siano supportati per un PK in EF.
Todd,

3

Avere una chiave di identità inutile a volte è inutile. Trovo se l'ID non viene utilizzato, perché aggiungerlo? Tuttavia, Entity non è così indulgente, quindi aggiungere un campo ID sarebbe la cosa migliore. Anche nel caso in cui non venga utilizzato, è meglio che gestire gli incessanti errori di Entity sulla chiave di identità mancante.


3

QUESTA SOLUZIONE FUNZIONA

Non è necessario mappare manualmente anche se non si dispone di un PK. Devi solo dire all'EF che una delle tue colonne è indice e la colonna indice non è nulla.

Per fare ciò puoi aggiungere un numero di riga alla tua vista con la funzione isNull come la seguente

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

ISNULL(id, number) è il punto chiave qui perché indica all'EF che questa colonna può essere la chiave primaria


2
Non consiglierei però la parte ISNULL. Se EF rileva due PK uguali, potrebbe non rendere il record univoco e restituire invece un oggetto condiviso. Questo mi è già successo.
Todd,

1
Devi usare isnull, altrimenti EF non crederà che non sia nulla.
Archlight,

2

Le risposte di cui sopra sono corrette se davvero non hai un PK.

Ma se ce n'è uno ma non è specificato con un indice nel DB e non è possibile modificare il DB (sì, lavoro nel mondo di Dilbert), è possibile mappare manualmente i campi in modo che siano la chiave.


2

Questa è solo un'aggiunta alla risposta di @Erick T. Se non esiste una singola colonna con valori univoci, la soluzione alternativa consiste nell'utilizzare una chiave composita, come indicato di seguito:

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

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

Ancora una volta, questa è solo una soluzione alternativa. La vera soluzione è correggere il modello di dati.


2

Forse è troppo tardi per rispondere ... comunque ...

Se una tabella non ha una chiave primaria, ci sono alcuni scenari che devono essere analizzati per far funzionare correttamente l'EF. La regola è: EF funzionerà con tabelle / classi con chiave primaria. Ecco come funziona il monitoraggio ...

Diciamo, la tua tabella 1. I record sono unici: l'unicità è fatta da una singola colonna di chiave esterna: 2. I record sono unici: l'unicità è fatta da una combinazione di più colonne. 3. I record non sono univoci (per la maggior parte *).

Per gli scenari n. 1 e n. 2 è possibile aggiungere la seguente riga al metodo OnModelCreating del modulo DbContext: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // tutte le colonne necessarie per rendere unici i record.

Per lo scenario n. 3 è ancora possibile utilizzare la soluzione sopra (n. 1 + n. 2) dopo aver studiato la tabella (* ciò che rende comunque tutti i record unici). Se è necessario includere TUTTE le colonne per rendere univoci tutti i record, è possibile aggiungere una colonna chiave primaria alla tabella. Se questa tabella proviene da un fornitore di terze parti, è necessario clonare questa tabella nel database locale (durante la notte o quante volte è necessario) con la colonna della chiave primaria aggiunta arbitrariamente tramite lo script del clone.


1
  1. Modifica la struttura della tabella e aggiungi una colonna primaria. Aggiorna il modello
  2. Modifica il file .EDMX in XML Editor e prova ad aggiungere una nuova colonna sotto il tag per questa tabella specifica (NON FUNZIONA)
  3. Invece di creare una nuova colonna primaria per uscire dalla tabella, creerò una chiave composita coinvolgendo tutte le colonne esistenti ( WORKED )

Entity Framework: aggiunta di DataTable senza chiave primaria al modello di entità.


Ho provato l'approccio chiave composito con EF 4.0 e non ha funzionato.
Ralph Willgoss,

Questo approccio ha funzionato perfettamente per me, può essere un dolore lavorare con i sistemi legacy "a volte" ...
pinmonkeyiii

1

Aggiorna alla risposta di @CodeNotFound .

In EF Core 3.0 DbQuery<T>è stato deprecato, invece dovresti usare tipi di entità Keyless che presumibilmente fanno la stessa cosa. Questi sono configurati con il HasNoKey()metodo ModelBuilder . Nella tua classe DbContext, esegui questa operazione

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

}

Vi sono tuttavia delle restrizioni, in particolare:

  • Non vengono mai tracciati per le modifiche nel DbContext e pertanto non vengono mai inseriti, aggiornati o eliminati nel database.
  • Supporta solo un sottoinsieme di funzionalità di mappatura della navigazione, in particolare:
    • Non possono mai fungere da fine principale di una relazione.
    • Potrebbero non avere navigazioni verso entità possedute
    • Possono contenere solo proprietà di navigazione di riferimento che puntano a entità regolari.
    • Le entità non possono contenere proprietà di navigazione verso tipi di entità senza chiave.

Ciò significa che per la questione di

Se voglio usarli e modificare i dati, devo necessariamente aggiungere un PK a quelle tabelle, oppure esiste una soluzione alternativa che non è necessario?

Non è possibile modificare i dati in questo modo, tuttavia è possibile leggere. Si potrebbe immaginare di usare un altro modo (ad esempio ADO.NET, Dapper) per modificare i dati - questa potrebbe essere una soluzione nei casi in cui raramente è necessario eseguire operazioni di non lettura e si vorrebbe comunque rimanere con EF Core per la maggior parte dei casi.

Inoltre, se hai davvero bisogno / vuoi lavorare con le tabelle heap (senza chiave), considera di abbandonare EF e usa un altro modo per parlare con il tuo database.


0

Da un punto di vista pratico, ogni tabella - anche una tabella denormalizzata come una tabella di magazzino - dovrebbe avere una chiave primaria. Oppure, in caso contrario, dovrebbe almeno avere un indice univoco e non annullabile.

Senza una sorta di chiave univoca, i record duplicati possono (e appariranno) apparire nella tabella, il che è molto problematico sia per i livelli ORM che per la comprensione di base dei dati. Una tabella con record duplicati è probabilmente un sintomo di cattiva progettazione.

Per lo meno, la tabella dovrebbe almeno avere una colonna identità. L'aggiunta di una colonna ID di generazione automatica richiede circa 2 minuti in SQL Server e 5 minuti in Oracle. Per quel minimo sforzo, molti, molti problemi saranno evitati.


La mia applicazione si trova in un'impostazione di data warehouse (con Oracle) e mi hai convinto a passare 5 minuti per aggiungere un indice. Ci vogliono davvero solo 5 minuti (o leggermente più se è necessario cercarlo o modificare ETL).
Trento,

0

Abbiamo riscontrato anche questo problema e, sebbene avessimo una colonna con valori Null, l'importante era che avessimo una colonna dipendente che non avesse valori Null e che la combinazione di queste due colonne fosse unica.

Quindi, per citare la risposta data da Pratap Reddy, ha funzionato bene per noi.




-8

La tabella deve contenere solo una colonna che non consente valori null

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.