121

По нескольким причинам, которые я не имею смелость говорить о том, мы определяем вид на нашем сервере базы данных Sql 2005 следующим образом:Entity Framework и SQL Server Просмотр

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 

Идея заключается в том, что Entity Framework создаст объект на основе этого запроса, который он делает, но он создает его с ошибкой, которая гласит следующее:

Предупреждение 6002: таблица/зрения «Keystone_Local.dbo.MeterProvingStatisticsPoint» не имеет первичный ключ. Ключ был выведен, и определение было создано как таблица/представление только для чтения.

И он решает, что поле CompletedDateTime будет этим первичным ключом этого объекта.

Мы используем EdmGen для создания модели. Есть ли способ, чтобы инфраструктура сущности не включала любое поле этого представления в качестве первичного ключа?

ответ

220

У нас была такая же проблема, и это решение:

Чтобы заставить рамки сущности использовать столбец в качестве первичного ключа, используйте ISNULL.

Чтобы заставить рамки сущности не использовать столбец в качестве первичного ключа, используйте NULLIF.

Простой способ применить это обернуть отборное заявление вашего зрения в другой выбор.

Пример:

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

Я думаю, что это лучшее, на что можно надеяться. В нижней части это работает. – MvcCmsJon

+0

Я пробовал это, и он не работает. Разрабатывает ли дизайнер EF определение представления или просто выводит столбцы из результатов данных? – sabanito

+1

Спасибо! Он отлично работал. @sabanito Я думаю, что это анализирует определение. поэтому вам нужно специально обернуть ключевые свойства в IsNull(). У меня есть представление, которое не возвращает никаких нулей (и не может возвращать нулевые значения), но из-за того, как была написана логика, EF не мог определить, что это было так, пока я не завернул ключи в IsNull(). – Rabbi

3
+0

Это имеет смысл. Итак, есть ли способ определить столбец как не нулевой или нулевой в представлении, как мы его определяем? –

+1

Извините, я уже выхожу из своего уровня знаний в Entity Framework. :-) – RBarryYoung

+1

Кто-нибудь знает, когда эта проблема будет исправлена? Раздражает необходимость обхода этого, когда у вас есть ненулевые столбцы, которые не являются первичными ключами. –

4

генератора тока Entity Framework EDM создаст составной ключ от всех ненулевых полей в представлении. Чтобы получить контроль над этим, вам нужно будет изменить представление и базовые столбцы таблицы, чтобы столбцы были обнуляемы, если вы не хотите, чтобы они были частью первичного ключа. Противоположность также верна, поскольку я столкнулся с тем, что сгенерированный ключ EDM вызывал проблемы с дублированием данных, поэтому мне пришлось определить столбец с нулевым значением как не имеющий значения null, чтобы заставить составной ключ в EDM включить этот столбец.

+0

У нас та же проблема с выведенным PK, сущность возвращает дублированные записи и полностью раздражает. Если вы выполняете 'Context.Entity.ToList()' дублирует записи, но если вы выполняете SQL-запрос, сгенерированный EF напрямую (полученный с помощью LINQPad), дублирование записи не происходит. Кажется, проблема связана с сопоставлением записей базы данных с возвращаемыми объектами сущностей (POCO), поскольку PK выводится с использованием объясненной логики (столбцы с нулевым значением). –

3

Чтобы получить представление, мне нужно было показать только столбец первичного ключа. Я создал второе представление, которое указывало на первый и использованный NULLIF, чтобы сделать типы нулевыми. Это помогло мне заставить EF думать, что в представлении есть только один первичный ключ.

Не уверены, если это поможет, хотя, так как я не считаю, что EF будет принимать объект с первичным ключом.

45

согласен с @Tillito, однако в большинстве случаев это будет загрязнять SQL оптимизатор, и он не будет использовать правильные индексы.

Это может быть очевидным для кого-то, но я сожжен часов, решая проблемы с производительностью, используя решение Tillito.Допустим, у вас есть таблица:

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

и ваш взгляд что-то вроде этого

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 оптимизатор не будет использовать индекс ix_customer и он будет выполнять сканирование таблицы по первичному индексу, но если вместо:

Group by CustomerId 

использовать

Group by IsNull(CustomerId, -1) 

это сделает MS SQL (не менее 2008) включите правый указатель в план.

+2

Это должен быть комментарий к ответу Тиллито, а не сам ответ, поскольку он не дает решения для вопроса OP. – zimdanen

+5

У парня есть репутация 1, он не может добавить комментарий. – jrcs3

+0

Спасибо за комментарий. –

7

Этот метод подходит для меня. Я использую ISNULL() для поля первичного ключа и COALESCE(), если поле не должно быть первичным ключом, но также должно иметь значение, не равное нулю. В этом примере выдается поле ID с первичным ключом, не подлежащим обнулению. Другие поля не являются ключами и имеют (Нет) как их атрибут 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 

, если вы действительно не имеют первичный ключ, вы можете подменить один с помощью ROW_NUMBER для создания псевдо-ключ, который игнорируется вашим кодом. Например:

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

Да, я закончил обманывать 'NEWID() как id', но это та же идея. И есть законные варианты использования - например, если у вас есть просмотр только для чтения. Уродливый, EF, уродливый. – ruffin

58

Я был в состоянии решить эту проблему с помощью конструктора.

  1. Открыть браузер моделей.
  2. Найдите вид на диаграмме.
  3. Щелкните правой кнопкой мыши по первичному ключу и убедитесь, что установлен флажок «Ключ сущности».
  4. Multi-select all non-primary keys. Используйте клавиши Ctrl или Shift.
  5. В окне «Свойства» (нажмите F4, если необходимо, чтобы увидеть его), измените выпадающий список «Entity Key» на False.
  6. Сохраните изменения.
  7. Закройте Visual Studio и откройте его. Я использую Visual Studio 2013 с EF 6 , и я должен был сделать это, чтобы убрать предупреждения.

Мне не нужно было менять свой вид, чтобы использовать обходные пути ISNULL, NULLIF или COALESCE. Если вы обновите свою модель из базы данных, предупреждения снова появятся, но исчезнут, если вы закроете и снова откроете VS. Изменения, внесенные вами в конструктор, будут сохранены и не затронуты обновлением.

+8

Подтверждено. Необходимо перезапустить VS2013, чтобы предупредить об этом. –

+5

Хотел бы я проголосовать за это несколько сотен раз. –

+3

«Вы пытались выключить и снова включить?» ;-) Спасибо, работает как шарм! –

2

Я также рекомендую, если вы не хотите связываться с тем, что должно быть основным ключом, чтобы включить ROW_NUMBER в ваш выбор и установить его как первичный ключ и установить все остальные столбцы/memebers как не первичные в модели.

1

Из-за вышеупомянутых проблем, я предпочитаю функции значения таблицы.

Если у вас есть это:

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

создать это:

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

Тогда вы просто импортировать функцию, а не мнение.

+1

Как бы вы создали ассоциации между объектами, идущими с этим подходом? Является ли это возможным? – ggderas

Смежные вопросы