Entity Framework i widok SQL Server

132

Z kilku powodów, o których nie mogę mówić, definiujemy widok naszej bazy danych Sql Server 2005 w następujący sposób:

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

Chodzi o to, że Entity Framework utworzy jednostkę na podstawie tego zapytania, co robi, ale generuje ją z błędem, który stwierdza, co następuje:

Ostrzeżenie 6002: Tabela / widok „Keystone_Local.dbo.MeterProvingStatisticsPoint” nie ma zdefiniowanego klucza podstawowego. Klucz został wywnioskowany, a definicja została utworzona jako tabela / widok tylko do odczytu.

I decyduje, że pole CompletedDateTime będzie kluczem podstawowym tej jednostki.

Używamy EdmGen do generowania modelu. Czy istnieje sposób, aby struktura encji nie zawierała żadnego pola tego widoku jako klucza podstawowego?

Sergio Romero
źródło

Odpowiedzi:

245

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
Tillito
źródło
2
Myślę, że jest to najlepsze, na co można mieć nadzieję. Podsumowując, to działa.
MvcCmsJon
1
Dziękuję Ci! Działało idealnie. @sabanito Myślę, że analizuje definicję. dlatego musisz specjalnie opakować właściwości klucza w IsNull (). Mam widok, który nie zwraca żadnych wartości null (i nie może zwracać żadnych wartości null), ale ze względu na sposób zapisania logiki EF nie mógł określić, że tak było, dopóki nie zawinąłem kluczy w IsNull ().
Rabin
3
Jedynym problemem, jaki tu widzę, jest to, że widok może zgodnie z prawem wymagać zwrócenia pustego ciągu „”. To, co zrobiłem, to po prostu rzutowanie kolumny z powrotem na jej własny typ danych. na przykład, gdyby AnotherProperty miało typ danych varchar (50), rzuciłbym go jako taki „CONVERT (VARCHAR (50), AnotherProperty) AS [AnotherProperty]”. to maskowało możliwość zerowania z EF, a także zezwalało na puste ciągi.
Bart
2
tak to działa, na przykład, aby EF używał kolumny jako klucza podstawowego isnull (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009
2
Oprócz tego, że w rozwiązaniu jest tylko irytująca wiadomość, czy nie naprawienie tego jest szkodliwe? Zgadzam się z twoim rozwiązaniem, ale szczerze mówiąc, nie czuję, że powinienem to robić - myślę, że wszyscy możemy się zgodzić, że to błąd, prawda?
dyslexicanaboko
67

Udało mi się to rozwiązać za pomocą projektanta.

  1. Otwórz przeglądarkę modeli.
  2. Znajdź widok na diagramie.
  3. Kliknij prawym przyciskiem myszy klucz podstawowy i upewnij się, że „Klucz jednostki” jest zaznaczony.
  4. Zaznacz wszystkie klucze inne niż podstawowe. Użyj klawiszy Ctrl lub Shift.
  5. W oknie Właściwości (w razie potrzeby naciśnij klawisz F4, aby je zobaczyć) zmień listę rozwijaną „Klucz jednostki” na Fałsz.
  6. Zapisz zmiany.
  7. Zamknij program Visual Studio i otwórz go ponownie. Używam programu Visual Studio 2013 z EF 6 i musiałem to zrobić, aby ostrzeżenia zniknęły.

Nie musiałem zmieniać widoku, aby użyć obejść ISNULL, NULLIF lub COALESCE. Jeśli zaktualizujesz model z bazy danych, ostrzeżenia pojawią się ponownie, ale znikną, jeśli zamkniesz i ponownie otworzysz VS. Zmiany wprowadzone w projektancie zostaną zachowane i odświeżenie nie będzie miało na nie wpływu.

Casey Plummer
źródło
9
Potwierdzony. Muszę ponownie uruchomić VS2013, aby ostrzeżenie zniknęło.
Michael Logutov
5
"Próbowałeś to wyłączyć i włączyć jeszcze raz?" ;-) Dzięki, działa jak marzenie!
Obl Tobl
4
Kiedy tworzę widoki, nie pojawiają się one nawet na diagramie modelu. Są komentowane w pliku xml
ggderas
Proste i łatwe rozwiązanie, które nie wydaje się tak skomplikowanym rozwiązaniem, jak manipulowanie widokiem! Dziękuję Ci.
LuqJensen
2
Potwierdzony VS2017 należy ponownie uruchomić, aby ostrzeżenie zniknęło.
Marc Levesque
46

Zgadzam się z @Tillito, jednak w większości przypadków będzie to zakłócać optymalizator SQL i nie będzie używał właściwych indeksów.

Może to być dla kogoś oczywiste, ale spędziłem godziny na rozwiązywaniu problemów z wydajnością za pomocą rozwiązania Tillito. Powiedzmy, że masz stół:

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

a twój pogląd jest mniej więcej taki

 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

Sql Optimizer nie użyje indeksu ix_customer i wykona skanowanie tabeli na indeksie podstawowym, ale jeśli zamiast:

Group by CustomerId

używasz

Group by IsNull(CustomerId, -1)

sprawi, że MS SQL (przynajmniej 2008) włączy odpowiedni indeks do planu.

Jeśli

Val Bakhtin
źródło
2
Powinien to być komentarz do odpowiedzi Tillito, a nie odpowiedź sama w sobie, ponieważ nie dostarcza rozwiązania na pytanie PO.
zimdanen
6
Facet ma rep 1, nie może jeszcze dodawać komentarza.
jrcs3
@zimdanen Nie ma możliwości, aby wszystkie te informacje znalazły się w komentarzu, bardziej sensowne jest umieszczenie ich w osobnej odpowiedzi.
Contango,
2
@Contango: Ta odpowiedź została zmieniona sześć dni po jej opublikowaniu, a ja opublikowałem swój komentarz. Zobacz historię zmian.
zimdanen
9

Ta metoda działa dobrze dla mnie. Używam ISNULL () dla pola klucza podstawowego i COALESCE (), jeśli pole nie powinno być kluczem podstawowym, ale powinno również mieć wartość nie dopuszczającą wartości null. Ten przykład daje pole identyfikatora z kluczem podstawowym niepodlegającym wartości null. Pozostałe pola nie są kluczami i mają atrybut (Brak) jako atrybut dopuszczający wartość zerową.

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

jeśli naprawdę nie masz klucza podstawowego, możesz go sfałszować, używając ROW_NUMBER do wygenerowania pseudoklucza, który jest ignorowany przez Twój kod. Na przykład:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE
SpazDude
źródło
Tak, skończyło się na tym, że oszukiwałem NEWID() as id, ale to ten sam pomysł. Są też uzasadnione przypadki użycia - na przykład jeśli masz widok tylko do odczytu. Brzydki, EF, brzydki.
ruffin
4

Bieżący generator Entity Framework EDM utworzy klucz złożony ze wszystkich pól niepodlegających wartości null w Twoim widoku. Aby uzyskać kontrolę nad tym, musisz zmodyfikować widok i kolumny tabeli bazowej, ustawiając kolumny na wartości null, jeśli nie chcesz, aby były częścią klucza podstawowego. Odwrotna prawda jest również prawdą, ponieważ napotkałem, że wygenerowany klucz EDM powodował problemy z duplikacją danych, więc musiałem zdefiniować kolumnę dopuszczającą wartość null jako nieprzekraczającą wartości null, aby zmusić klucz złożony w EDM do uwzględnienia tej kolumny.

Annagram
źródło
Mamy ten sam problem z wywnioskowanym PK, jednostka zwraca zduplikowane rekordy i jest to kompletnie irytujące. Jeśli wykonaszContext.Entity.ToList() zduplikowane rekordy, ale jeśli wykonasz zapytanie SQL wygenerowane przez EF bezpośrednio (uzyskane za pomocą LINQPad), nie nastąpi duplikacja rekordów. Wydaje się, że występuje problem z mapowaniem rekordów bazy danych na zwrócone obiekty jednostki (POCO), ponieważ PK jest wywnioskowana przy użyciu wyjaśnionej logiki (kolumny bez wartości null).
David Oliván Ubieto
3

Wygląda na to, że jest to znany problem z EdmGen: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/

RBarryYoung
źródło
To ma sens. Czy jest więc sposób na zdefiniowanie kolumny jako niepustej lub zerowej w widoku w taki sposób, w jaki ją definiujemy?
Sergio Romero
1
Przepraszam, jestem już poza moim poziomem wiedzy w Entity Framework. :-)
RBarryYoung
1
Czy ktoś wie, kiedy ten problem zostanie rozwiązany? Irytujące jest obejście tego problemu, gdy masz kolumny inne niż null, które nie są kluczami podstawowymi.
miłość na żywo
3

Aby uzyskać widok, musiałem pokazać tylko jeden kolumnę klucza podstawowego, utworzyłem drugi widok, który wskazywał na pierwszy i użyłem NULLIF, aby nadać typom wartość null. Pomogło mi to, aby EF pomyślał, że w widoku jest tylko jeden klucz podstawowy.

Nie jestem pewien, czy to ci pomoże, ponieważ nie wierzę, że EF zaakceptuje jednostkę bez klucza podstawowego.

Nick Gotch
źródło
3

Jeśli nie chcesz bawić się kluczem podstawowym, polecam:

  1. Włączać ROW_NUMBER do swojego wyboru
  2. Ustaw go jako klucz podstawowy
  3. Ustaw wszystkie inne kolumny / elementy jako inne niż podstawowe w modelu
Santhos
źródło
1

Ze względu na powyższe problemy preferuję funkcje tabelaryczne.

Jeśli masz to:

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

utwórz to:

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

Następnie po prostu importujesz funkcję, a nie widok.

Promień
źródło
2
Jak stworzyłbyś skojarzenia między podmiotami stosującymi to podejście? Czy to możliwe?
ggderas