Entity Framework и SQL Server View - PullRequest
130 голосов
/ 18 июня 2009

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

Ответы [ 9 ]

237 голосов
/ 26 апреля 2010

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

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

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

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

Пример:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp
65 голосов
/ 18 января 2014

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

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

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

45 голосов
/ 04 мая 2012

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

Если

8 голосов
/ 30 января 2013

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

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

3 голосов
/ 23 февраля 2010

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

Не уверен, поможет ли это вам, хотя, поскольку я не верю, что EF примет объект без НИКАКОГО первичного ключа.

3 голосов
/ 18 июня 2009
2 голосов
/ 25 февраля 2014

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

1 голос
/ 04 августа 2014

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

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

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

создать это:

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

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

...