Entity Framework und SQL Server-Ansicht

132

Aus mehreren Gründen, über die ich nicht sprechen kann, definieren wir eine Ansicht in unserer SQL Server 2005-Datenbank wie folgt:

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

Die Idee ist, dass das Entity Framework eine Entität basierend auf dieser Abfrage erstellt, was es jedoch tut, aber es mit einem Fehler generiert, der Folgendes angibt:

Warnung 6002: In der Tabelle / Ansicht 'Keystone_Local.dbo.MeterProvingStatisticsPoint' ist kein Primärschlüssel definiert. Der Schlüssel wurde abgeleitet und die Definition als schreibgeschützte Tabelle / Ansicht erstellt.

Und es wird entschieden, dass das Feld CompletedDateTime dieser Entitätsprimärschlüssel ist.

Wir verwenden EdmGen, um das Modell zu generieren. Gibt es eine Möglichkeit, dass das Entity-Framework kein Feld dieser Ansicht als Primärschlüssel enthält?

Sergio Romero
quelle

Antworten:

245

Wir hatten das gleiche Problem und dies ist die Lösung:

Verwenden Sie ISNULL, um das Entity Framework zu zwingen, eine Spalte als Primärschlüssel zu verwenden.

Verwenden Sie NULLIF, um zu erzwingen, dass das Entity Framework keine Spalte als Primärschlüssel verwendet.

Eine einfache Möglichkeit, dies anzuwenden, besteht darin, die select-Anweisung Ihrer Ansicht in eine andere select-Anweisung zu verpacken.

Beispiel:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp
Tillito
quelle
2
Ich denke, das ist das Beste, auf das man hoffen kann. Unterm Strich funktioniert es.
MvcCmsJon
1
Danke! Es hat perfekt funktioniert. @ Sabanito Ich denke, es analysiert die Definition. Aus diesem Grund müssen Sie die Schlüsseleigenschaften in IsNull () speziell umschließen. Ich habe eine Ansicht, die keine Nullen zurückgibt (und keine Nullen zurückgeben kann), aber aufgrund der Art und Weise, wie die Logik geschrieben wurde, konnte EF nicht feststellen, dass dies der Fall war, bis ich die Schlüssel in IsNull () eingewickelt habe.
Rabbi
3
Das einzige Problem, das ich hier sehe, ist, dass die Ansicht möglicherweise eine leere Zeichenfolge '' zurückgeben muss. Ich habe die Spalte einfach auf ihren eigenen Datentyp zurückgesetzt. Wenn AnotherProperty beispielsweise den Datentyp varchar (50) hätte, würde ich ihn als solchen "CONVERT (VARCHAR (50), AnotherProperty) AS [AnotherProperty]" umwandeln. Dies maskierte die Nullfähigkeit von EF und erlaubte auch leere Zeichenfolgen.
Bart
2
Ja, dies funktioniert zum Beispiel, damit EF die Spalte als Primärschlüssel verwendet. isnull (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009
2
Abgesehen davon, dass die Lösung nur eine nervige Meldung enthält, kann es schaden, dies nicht zu beheben? Ich stimme Ihrer Lösung zu, aber ehrlich gesagt habe ich nicht das Gefühl, dass ich das tun muss - ich denke, wir können uns alle einig sein, dass dies ein Fehler ist, oder?
Legasthenikeraboko
67

Ich konnte dies mit dem Designer lösen.

  1. Öffnen Sie den Modellbrowser.
  2. Finden Sie die Ansicht im Diagramm.
  3. Klicken Sie mit der rechten Maustaste auf den Primärschlüssel und stellen Sie sicher, dass "Entitätsschlüssel" aktiviert ist.
  4. Mehrfachauswahl aller nicht primären Schlüssel. Verwenden Sie die Strg- oder Umschalttaste.
  5. Ändern Sie im Eigenschaftenfenster (drücken Sie bei Bedarf F4, um es anzuzeigen) die Dropdown-Liste "Entitätsschlüssel" in "Falsch".
  6. Änderungen speichern.
  7. Schließen Sie Visual Studio und öffnen Sie es erneut. Ich verwende Visual Studio 2013 mit EF 6 und musste dies tun, damit die Warnungen verschwinden.

Ich musste meine Ansicht nicht ändern, um die Problemumgehungen ISNULL, NULLIF oder COALESCE zu verwenden. Wenn Sie Ihr Modell aus der Datenbank aktualisieren, werden die Warnungen erneut angezeigt, verschwinden jedoch, wenn Sie VS schließen und erneut öffnen. Die im Designer vorgenommenen Änderungen bleiben erhalten und werden von der Aktualisierung nicht beeinflusst.

Casey Plummer
quelle
9
Bestätigt. Sie müssen VS2013 neu starten, damit die Warnung verschwindet.
Michael Logutov
5
"Haben Sie versucht, es aus- und wieder einzuschalten?" ;-) Danke, funktioniert wie ein Zauber!
Obl Tobl
4
Wenn ich Ansichten erstelle, werden sie nicht einmal im Modelldiagramm angezeigt. Sie sind in der XML-Datei kommentiert
ggderas
Einfache und unkomplizierte Lösung und scheint nicht so ein Nono-Hacky-Fix ​​zu sein, wie die Ansicht zu manipulieren! Danke dir.
LuqJensen
2
Bestätigt VS2017 muss neu gestartet werden, damit die Warnung nicht mehr angezeigt wird.
Marc Levesque
46

Stimmen Sie mit @Tillito überein, aber in den meisten Fällen wird der SQL-Optimierer verschmutzt und es werden keine richtigen Indizes verwendet.

Es mag für jemanden offensichtlich sein, aber ich habe Stunden damit verbracht, Leistungsprobleme mit der Tillito-Lösung zu lösen. Nehmen wir an, Sie haben den Tisch:

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

und Ihre Ansicht ist so etwas

 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

Das SQL-Optimierungsprogramm verwendet nicht den Index ix_customer und führt einen Tabellenscan für den Primärindex durch.

Group by CustomerId

Sie nutzen

Group by IsNull(CustomerId, -1)

Dadurch wird MS SQL (mindestens 2008) den richtigen Index in den Plan aufnehmen.

Wenn

Val Bakhtin
quelle
2
Dies sollte ein Kommentar zu Tillitos Antwort sein, keine Antwort selbst, da dies keine Lösung für die Frage des OP darstellt.
Zimdanen
6
Der Typ hat eine Wiederholung von 1, er kann noch keinen Kommentar hinzufügen.
jrcs3
@zimdanen Es gibt keine Möglichkeit, all diese Informationen in einen Kommentar einzufügen. Es ist sinnvoller, sie in einer separaten Antwort zu haben.
Contango
2
@Contango: Diese Antwort wurde sechs Tage nach dem Posten bearbeitet und ich habe meinen Kommentar gepostet. Siehe den Revisionsverlauf.
Zimdanen
9

Diese Methode funktioniert gut für mich. Ich verwende ISNULL () für das Primärschlüsselfeld und COALESCE (), wenn das Feld nicht der Primärschlüssel sein soll, sondern auch einen nicht nullbaren Wert haben soll. Dieses Beispiel liefert ein ID-Feld mit einem nicht nullbaren Primärschlüssel. Die anderen Felder sind keine Schlüssel und haben (Null) als Nullable-Attribut.

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

Wenn Sie wirklich keinen Primärschlüssel haben, können Sie einen fälschen, indem Sie mit ROW_NUMBER einen Pseudoschlüssel generieren, der von Ihrem Code ignoriert wird. Beispielsweise:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE
SpazDude
quelle
Ja, am Ende habe ich geschummelt NEWID() as id, aber es ist die gleiche Idee. Und es gibt legitime Anwendungsfälle - zum Beispiel, wenn Sie eine schreibgeschützte Ansicht haben. Hässlich, EF, hässlich.
Ruffin
4

Der aktuelle Entity Framework-EDM-Generator erstellt einen zusammengesetzten Schlüssel aus allen nicht nullbaren Feldern in Ihrer Ansicht. Um die Kontrolle darüber zu erlangen, müssen Sie die Ansicht und die zugrunde liegenden Tabellenspalten ändern und die Spalten auf null setzen, wenn Sie nicht möchten, dass sie Teil des Primärschlüssels sind. Das Gegenteil ist auch der Fall, da der vom EDM generierte Schlüssel Probleme beim Duplizieren von Daten verursachte. Daher musste ich eine nullfähige Spalte als nicht nullfähig definieren, um zu erzwingen, dass der zusammengesetzte Schlüssel im EDM diese Spalte enthält.

Annagramm
quelle
Wir haben das gleiche Problem mit der abgeleiteten PK, die Entität gibt doppelte Datensätze zurück und ist völlig ärgerlich. Wenn Sie Context.Entity.ToList()doppelte Datensätze ausführen, die von EF generierte SQL-Abfrage jedoch direkt ausführen (mit LINQPad erhalten), erfolgt keine doppelte Datensätze. Scheint ein Problem zu sein, die Datenbankeinträge den zurückgegebenen Entitätsobjekten (POCO) zuzuordnen, da die PK unter Verwendung der erläuterten Logik (nicht nullbare Spalten) abgeleitet wird.
David Oliván Ubieto
3

Es scheint ein bekanntes Problem mit EdmGen zu sein: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/

RBarryYoung
quelle
Das macht Sinn. Gibt es also eine Möglichkeit, eine Spalte in einer Ansicht so zu definieren, dass sie nicht null oder null ist, wie wir sie definieren?
Sergio Romero
1
Entschuldigung, ich bin bereits über mein Fachwissen in Entity Framework hinaus. :-)
RBarryYoung
1
Weiß jemand, wann dieses Problem behoben wird? Es ist ärgerlich, dies umgehen zu müssen, wenn Sie Spalten ohne Null haben, die keine Primärschlüssel sind.
Live-Liebe
3

Um eine Ansicht zu erhalten, musste ich nur eine Primärschlüsselspalte anzeigen. Ich habe eine zweite Ansicht erstellt, die auf die erste zeigt, und NULLIF verwendet, um die Typen auf Null zu setzen. Dies hat für mich funktioniert, um die EF glauben zu lassen, dass nur ein einziger Primärschlüssel in der Ansicht enthalten ist.

Ich bin mir nicht sicher, ob dies Ihnen helfen wird, da ich nicht glaube, dass die EF eine Entität ohne Primärschlüssel akzeptiert.

Nick Gotch
quelle
3

Wenn Sie sich nicht mit dem Primärschlüssel anlegen möchten, empfehle ich:

  1. Übernehmen ROW_NUMBER in Ihre Auswahl
  2. Legen Sie es als Primärschlüssel fest
  3. Legen Sie alle anderen Spalten / Elemente im Modell als nicht primär fest
Santhos
quelle
1

Aufgrund der oben genannten Probleme bevorzuge ich Tabellenwertfunktionen.

Wenn Sie dies haben:

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

Erstellen Sie dies:

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

Dann importieren Sie einfach die Funktion und nicht die Ansicht.

Strahl
quelle
2
Wie würden Sie mit diesem Ansatz Assoziationen zwischen Unternehmen herstellen? Ist es möglich?
Ggderas