Представление Entity Framework и представление SQL Server

По нескольким причинам, о которых я не имею права говорить, мы определяем представление о нашей базе данных 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 для генерации модели. Есть ли способ, чтобы инфраструктура сущности не включала любое поле этого представления в качестве первичного ключа?

Ответ 1

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

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

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

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

Пример:

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

Ответ 2

Мне удалось решить это с помощью конструктора.

  • Откройте обозреватель моделей.
  • Найдите представление на диаграмме.
  • Щелкните правой кнопкой мыши по первичному ключу и убедитесь, что установлен флажок "Ключ сущности".
  • Мульти-выберите все непервичные ключи. Используйте клавиши Ctrl или Shift.
  • В окне "Свойства" (нажмите F4, если необходимо, чтобы увидеть его), измените "Ключ сущности" выпадающий список - "Неверно".
  • Сохранить изменения.
  • Закройте Visual Studio и откройте его. Я использую Visual Studio 2013 с EF 6 и я должен был сделать это, чтобы убрать предупреждения.

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

Ответ 3

Согласитесь с @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) включить правый указатель в план.

Если

Ответ 4

Этот метод работает хорошо для меня. Я использую 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

Ответ 5

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

Ответ 7

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

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

Ответ 8

Если вы не хотите связываться с тем, что должно быть первичным ключом, я рекомендую:

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

Ответ 9

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

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

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

создайте это:

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

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