Entity Framework та перегляд SQL Server


132

З кількох причин, про які я не маю права говорити, ми визначаємо погляд на нашу базу даних Sql Server 2005 таким чином:

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 для генерування моделі. Чи є спосіб не мати рамки сутності включати будь-яке поле цього перегляду в якості основного ключа?

Відповіді:


245

У нас була така ж проблема, і це рішення:

Щоб змусити структуру сутності використовувати стовпчик як основний ключ, використовуйте ISNULL.

Щоб змусити структуру сутності не використовувати стовпчик як основний ключ, використовуйте NULLIF.

Простий спосіб застосувати це - перенести вибраний вигляд вашого перегляду в інший вибір.

Приклад:

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

2
Я думаю, що це найкраще, на що можна сподіватися. Підсумок це працює.
MvcCmsJon

1
Спасибі! Це спрацювало чудово. @sabanito Я думаю, що це аналізує визначення. тому вам потрібно спеціально обернути ключові властивості в IsNull (). У мене є погляд, який не повертає жодних нулів (і не може повернути жодних нулей), але через спосіб написання логіки, EF не міг визначити, що це було так, поки я не загорнув ключі в IsNull ().
Рабин

3
Єдине, що я бачу тут, - це те, що перегляд може законно потребувати повернення порожнього рядка ''. Що я зробив, було просто повернути стовпчик до власного типу даних. наприклад, якби у OtherProperty був тип даних varchar (50), я б надавав це як "CONVERT (VARCHAR (50), AnotherProperty) AS" [AnotherProperty] ". це маскувало нульовість від EF, а також дозволяло порожні рядки.
Барт

2
так, це працює, наприклад, для того, щоб EF використовувати стовпчик як основний ключ isnull (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009

2
Окрім того, що у рішенні є лише дратівливе повідомлення, чи є шкоду у тому, щоб не виправити це? Я згоден з вашим рішенням, але, чесно кажучи, я не відчуваю, що мені це потрібно робити - я думаю, що ми можемо всі погодитись, що це помилка?
dislexicanaboko

67

Мені вдалося вирішити це за допомогою конструктора.

  1. Відкрийте браузер моделей.
  2. Знайдіть вид на схемі.
  3. Клацніть правою кнопкою миші на первинному ключі та переконайтесь, що "Entity Key" встановлено.
  4. Вибір кількох клавіш усіх не первинних клавіш. Використовуйте клавіші Ctrl або Shift.
  5. У вікні "Властивості" (натисніть клавішу F4, щоб побачити це), змініть спадне меню "Ключ особи" на "Неправильне".
  6. Зберегти зміни.
  7. Закрийте Visual Studio і відкрийте її знову. Я використовую Visual Studio 2013 з EF 6, і мені довелося це зробити, щоб отримати попередження про відхід.

Мені не довелося змінювати свій погляд на використання ISNULL, NULLIF або COALESCE обхідних шляхів. Якщо ви оновлюєте свою модель з бази даних, попередження знову з’являться, але відпадають, якщо закрити і повторно відкрити VS. Зміни, які ви внесли в дизайнер, будуть збережені та не впливатимуть на оновлення.


9
Підтверджено. Потрібно перезапустити VS2013, щоб запобігти відключенню попередження.
Михайло Логутов

5
"Ви намагалися вимкнути та знову ввімкнути?" ;-) Дякую, працює як шарм!
Обл Тобл

4
Коли я створюю представлення, вони навіть не входять у схему моделі. Вони коментуються у xml-файлі
ggderas

Просте і легке рішення, і не здається стільки неповторним виправленням, скільки маніпулюванням видом! Дякую.
LuqJensen

2
Підтверджений VS2017 потрібно перезапустити також для попередження про відмову.
Марк Левеск

46

Погодьтеся з @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
Це має бути коментарем до відповіді Тілліто, а не самою відповіддю, оскільки це не дає рішення на питання ОП.
zimdanen

6
У хлопця є відповідь 1, він поки що не може додати коментар.
jrcs3

@zimdanen Ні в якому разі не можна було б помістити всю цю інформацію в коментар, є більш сенсом мати її в окремій відповіді.
Контанго

2
@Contango: Ця відповідь була змінена через шість днів після її розміщення, і я розмістив свій коментар. Перегляньте історію редагування.
zimdanen

9

Цей метод добре працює для мене. Я використовую ISNULL () для поля первинного ключа, і COALESCE (), якщо поле не повинно бути первинним ключем, але також повинно мати ненульове значення. Цей приклад дає поле ID з ненульовим первинним ключем. Інші поля не є ключами та мають (None) як атрибут 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

Так, я закінчив обман NEWID() as id, але це та сама ідея. І є законні випадки використання - наприклад, якщо у вас є режим перегляду лише для читання. Некрасивий, EF, некрасивий.
ruffin

4

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


У нас така ж проблема з виведеним ПК, суб'єкт повертає дублювані записи і абсолютно дратує. Якщо ви виконуєте Context.Entity.ToList()дублікати записів, але якщо ви виконуєте запит SQL, згенерований EF безпосередньо (отриманий за допомогою LINQPad), дублювання записів не відбувається. Виглядає проблема з відображенням записів бази даних до повернених об'єктів об'єктів (POCO), оскільки ПК виводиться за допомогою поясненої логіки (стовпчики, що не змінюються на нуль).
Девід Оліван Убієто

3

Що має сенс. Отже, чи існує спосіб визначити стовпець як ненульовий або нульовий у представленні так, як ми його визначаємо?
Серхіо Ромеро

1
Вибачте, я вже перевищую свій рівень знань щодо Entity Framework. :-)
RBarryYoung

1
Хтось знає, коли це питання буде вирішено? Прикро, що вам доведеться вирішити це, коли у вас є ненулі стовпці, які не є первинними ключами.
live-love

3

Щоб отримати представлення, мені довелося показати лише один стовпчик первинного ключа, я створив другий вигляд, який вказував на перший, і використовував NULLIF, щоб зробити типи нульовими. Це допомогло мені змусити ЕФ подумати, що існує лише один основний ключ.

Не впевнений, чи допоможе це вам, хоча я не вірю, що EF прийме сутність без основного ключа.


3

Якщо ви не хочете возитися з тим, що має бути первинним ключем, рекомендую:

  1. Включіть ROW_NUMBERу свій вибір
  2. Встановіть його як основний ключ
  3. Встановіть усі інші стовпці / члени як первинні в моделі

1

Через вищезазначені проблеми я віддаю перевагу функціям таблиці значень.

Якщо у вас є це:

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

створити це:

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

Тоді ви просто імпортуєте функцію, а не перегляд.


2
Як би ви створили асоціації серед організацій, що дотримуються такого підходу? Це можливо?
ggderas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.