Entity Framework e SQL Server View


132

Per diversi motivi di cui non ho la libertà di cui parlare, stiamo definendo una vista sul nostro database SQL Server 2005 in questo modo:

CREATE VIEW [dbo].[MeterProvingStatisticsPoint]
AS
SELECT
    CAST(0 AS BIGINT) AS 'RowNumber',
    CAST(0 AS BIGINT) AS 'ProverTicketId',
    CAST(0 AS INT) AS 'ReportNumber',
    GETDATE() AS 'CompletedDateTime',
    CAST(1.1 AS float) AS 'MeterFactor',
    CAST(1.1 AS float) AS 'Density',
    CAST(1.1 AS float) AS 'FlowRate',
    CAST(1.1 AS float) AS 'Average',
    CAST(1.1 AS float) AS 'StandardDeviation',
    CAST(1.1 AS float) AS 'MeanPlus2XStandardDeviation',
    CAST(1.1 AS float) AS 'MeanMinus2XStandardDeviation'
WHERE 0 = 1

L'idea è che Entity Framework creerà un'entità in base a questa query, cosa che fa, ma la genera con un errore che indica quanto segue:

Avviso 6002: la tabella / vista "Keystone_Local.dbo.MeterProvingStatisticsPoint" non ha una chiave primaria definita. La chiave è stata dedotta e la definizione è stata creata come tabella / vista di sola lettura.

E decide che il campo CompletedDateTime sarà questa chiave primaria dell'entità.

Stiamo usando EdmGen per generare il modello. Esiste un modo per evitare che il framework di entità includa un campo di questa vista come chiave primaria?

Risposte:


245

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

2
Penso che questo sia il migliore da sperare. In conclusione funziona.
MvcCmsJon

1
Grazie! Ha funzionato perfettamente. @sabanito Penso che analizzi la definizione. ecco perché è necessario racchiudere in modo specifico le proprietà chiave in IsNull (). Ho una visione che non restituisce alcun nullo (e non può restituire alcun nullo) ma a causa del modo in cui è stata scritta la logica, EF non è riuscito a determinare che fosse così fino a quando non ho avvolto le chiavi in ​​IsNull ().
Rabbi,

3
L'unico problema che vedo qui è che la vista potrebbe legittimamente dover restituire una stringa vuota ''. Quello che ho fatto è stato semplicemente riportare la colonna sul suo tipo di dati. ad esempio, se AnotherProperty avesse un tipo di dati varchar (50), lo lancerei come "CONVERT (VARCHAR (50), AnotherProperty) AS [AnotherProperty]". questo ha mascherato il nullability da EF e ha permesso anche stringhe vuote.
Bart,

2
sì, questo funziona ad esempio per fare in modo che EF utilizzi la colonna come chiave primaria isnull (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009

2
A parte il fatto che esiste un messaggio fastidioso nella soluzione, c'è qualche danno nel non risolvere questo problema? Sono d'accordo con la tua soluzione, ma sinceramente non sento che dovrei farlo - penso che tutti possiamo essere d'accordo sul fatto che si tratta di un bug giusto?
dyslexicanaboko,

67

Sono stato in grado di risolvere questo problema utilizzando il designer.

  1. Apri il Browser modello.
  2. Trova la vista nel diagramma.
  3. Fare clic con il tasto destro del mouse sulla chiave primaria e assicurarsi che "Entity Key" sia selezionato.
  4. Seleziona più tutte le chiavi non primarie. Utilizzare i tasti Ctrl o Maiusc.
  5. Nella finestra Proprietà (premere F4 se necessario per vederlo), modificare il menu a discesa "Chiave entità" su Falso.
  6. Salvare le modifiche.
  7. Chiudi Visual Studio e riaprilo. Sto usando Visual Studio 2013 con EF 6 e ho dovuto farlo per far sparire gli avvisi.

Non ho dovuto modificare la mia visione per utilizzare le soluzioni alternative ISNULL, NULLIF o COALESCE. Se aggiorni il tuo modello dal database, gli avvisi riappariranno, ma scompariranno se chiudi e riapri VS. Le modifiche apportate alla finestra di progettazione verranno mantenute e non saranno influenzate dall'aggiornamento.


9
Confermato. Per riavviare l'avviso, è necessario riavviare VS2013.
Michael Logutov,

5
"Hai provato a spegnere e riaccendere?" ;-) Grazie, funziona come un fascino!
Obl Tobl,

4
Quando creo viste, non riescono nemmeno a essere nel diagramma del modello. Sono commentati nel file xml
ggderas,

Soluzione semplice e facile e non sembra tanto una correzione non complicata quanto manipolare la vista! Grazie.
LuqJensen,

2
Il VS2017 confermato deve essere riavviato anche perché l'avviso scompaia.
Marc Levesque,

46

Concordo con @Tillito, tuttavia nella maggior parte dei casi sporcherà l'ottimizzatore SQL e non utilizzerà gli indici corretti.

Può essere ovvio per qualcuno, ma ho bruciato ore a risolvere problemi di prestazioni usando la soluzione Tillito. Diciamo che hai il tavolo:

 Create table OrderDetail
    (  
       Id int primary key,
       CustomerId int references Customer(Id),
       Amount decimal default(0)
    );
 Create index ix_customer on OrderDetail(CustomerId);

e la tua visione è qualcosa del genere

 Create view CustomerView
    As
      Select 
          IsNull(CustomerId, -1) as CustomerId, -- forcing EF to use it as key
          Sum(Amount) as Amount
      From OrderDetail
      Group by CustomerId

L'ottimizzatore SQL non utilizzerà l'indice ix_customer e eseguirà la scansione della tabella sull'indice primario, ma se invece di:

Group by CustomerId

usate

Group by IsNull(CustomerId, -1)

farà in modo che MS SQL (almeno il 2008) includa l'indice giusto nel piano.

Se


2
Questo dovrebbe essere un commento sulla risposta di Tillito, non una risposta in sé, poiché non fornisce una soluzione alla domanda del PO.
zimdanen,

6
Il ragazzo ha un rappresentante di 1, non può ancora aggiungere un commento.
jrcs3,

@zimdanen Non c'è modo di inserire tutte queste informazioni in un commento, ha più senso averle in una risposta separata.
Contango

2
@Contango: questa risposta è stata modificata sei giorni dopo che è stata pubblicata e ho pubblicato il mio commento. Vedi la cronologia delle revisioni.
zimdanen,

9

Questo metodo funziona bene per me. Uso ISNULL () per il campo chiave primaria e COALESCE () se il campo non deve essere la chiave primaria, ma dovrebbe anche avere un valore non annullabile. In questo esempio viene generato un campo ID con una chiave primaria non annullabile. Gli altri campi non sono chiavi e hanno (Nessuno) come attributo Nullable.

SELECT      
ISNULL(P.ID, - 1) AS ID,  
COALESCE (P.PurchaseAgent, U.[User Nickname]) AS PurchaseAgent,  
COALESCE (P.PurchaseAuthority, 0) AS PurchaseAuthority,  
COALESCE (P.AgencyCode, '') AS AgencyCode,  
COALESCE (P.UserID, U.ID) AS UserID,  
COALESCE (P.AssignPOs, 'false') AS AssignPOs,  
COALESCE (P.AuthString, '') AS AuthString,  
COALESCE (P.AssignVendors, 'false') AS AssignVendors 
FROM Users AS U  
INNER JOIN Users AS AU ON U.Login = AU.UserName  
LEFT OUTER JOIN PurchaseAgents AS P ON U.ID = P.UserID

se in realtà non hai una chiave primaria, puoi falsificarne una usando ROW_NUMBER per generare una pseudo-chiave che viene ignorata dal tuo codice. Per esempio:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE

Sì, ho finito per barare NEWID() as id, ma è la stessa idea. E ci sono casi d'uso legittimi, ad esempio se hai una vista di sola lettura. Brutto, EF, brutto.
ruffin,

4

L'attuale generatore EDM Entity Framework creerà una chiave composita da tutti i campi non annullabili nella vista. Per ottenere il controllo su questo, è necessario modificare la vista e le colonne della tabella sottostante impostando le colonne su nullable quando non si desidera che facciano parte della chiave primaria. È vero anche il contrario, come ho riscontrato, la chiave generata da EDM stava causando problemi di duplicazione dei dati, quindi ho dovuto definire una colonna nullable come non nullable per forzare la chiave composita nell'EDM a includere quella colonna.


Abbiamo lo stesso problema con il PK inferito, l'entità restituisce record duplicati ed è completamente fastidiosa. Se si eseguono Context.Entity.ToList()record duplicati, ma se si esegue direttamente la query SQL generata da EF (ottenuta con LINQPad), non si verifica alcuna duplicazione dei record. Sembra essere un problema nel mappare i record del database sugli oggetti entità (POCO) restituiti, poiché il PK viene dedotto usando la logica spiegata (colonne non annullabili).
David Oliván Ubieto,

3

Questo ha senso. Quindi, c'è un modo per definire una colonna come null o null in una vista come la stiamo definendo?
Sergio Romero,

1
Siamo spiacenti, sono già oltre il mio livello di competenza in Entity Framework. :-)
RBarryYoung

1
Qualcuno sa quando questo problema verrà risolto? È fastidioso dover risolvere questo problema quando si hanno colonne non nulle che non sono chiavi primarie.
live-love

3

Per ottenere una vista ho dovuto mostrare solo una colonna chiave primaria ho creato una seconda vista che puntava alla prima e ho usato NULLIF per rendere i tipi nulla. Questo ha funzionato per me per far pensare all'EF che ci fosse una sola chiave primaria nella vista.

Non sono sicuro se questo ti aiuterà, poiché non credo che l'EF accetterà un'entità senza chiave primaria.


3

Se non vuoi fare confusione con quella che dovrebbe essere la chiave primaria, ti consiglio di:

  1. Incorporare ROW_NUMBERnella selezione
  2. Impostalo come chiave primaria
  3. Impostare tutte le altre colonne / membri come non primari nel modello

1

A causa dei problemi di cui sopra, preferisco le funzioni dei valori di tabella.

Se hai questo:

CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something

crea questo:

CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])

Quindi importa semplicemente la funzione anziché la vista.


2
Come creereste associazioni tra entità che seguono questo approccio? È possibile?
Ggderas,
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.