Представление 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.ДБО.MeterProvingStatisticsPoint ' не имеет определенного первичного ключа. Ключ был выведен и определение было создано как таблица/представление только для чтения.
и он решает, что поле CompletedDateTime будет этим первичным ключом сущности.
мы используем EdmGen для создания модели. Есть ли способ не включать в Entity framework какое-либо поле этого представления в качестве первичного ключа?
9 ответов:
у нас была такая же проблема и это решение:
чтобы заставить Entity framework использовать столбец в качестве первичного ключа, используйте ISNULL.
чтобы заставить Entity framework не использовать столбец в качестве первичного ключа, используйте NULLIF.
простой способ применить это-обернуть оператор select вашего представления в другой select.
пример:
SELECT ISNULL(MyPrimaryID,-999) MyPrimaryID, NULLIF(AnotherProperty,'') AnotherProperty FROM ( ... ) AS temp
Я смог решить эту проблему с помощью конструктора.
- откройте браузер модели.
- Найдите вид на диаграмме.
- щелкните правой кнопкой мыши на первичном ключе и убедитесь, что установлен флажок "ключ сущности".
- множественный выбор всех непервичных ключей. Используйте клавиши Ctrl или Shift.
- в окне свойства (при необходимости нажмите клавишу F4) измените значение Раскрывающийся список "ключ сущности" до False.
- Сохранить изменения.
- Закрыть Visual Студия и снова открыть его. Я использую Visual Studio 2013 с EF 6 и я должен был сделать это, чтобы получить предупреждения, чтобы уйти.
Мне не пришлось менять свое мнение, чтобы использовать isnull, NULLIF или COALESCE обходные пути. Если вы обновить модель из базы данных, предупреждения вновь появится, но исчезнет, если закрыть и повторно открыть против. Изменения, внесенные в конструкторе будут сохранены и не затронуты обновления.
согласен с @Tillito, однако в большинстве случаев это будет фол SQL optimizer и он не будет использовать правильные индексы.
это может быть очевидно для кого-то, но я сжег часы, решая проблемы с производительностью, используя решение 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) включить правильный индекс в план.
если
этот метод хорошо работает для меня. Я использую ISNULL () для поля первичного ключа и COALESCE (), если поле не должно быть первичным ключом, но также должно иметь значение, не допускающее значения null. Этот пример дает поле 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
текущий генератор EDM Entity Framework создаст составной ключ из всех полей, не имеющих значения NULL в вашем представлении. Чтобы получить контроль над этим, вам нужно будет изменить представление и базовые столбцы таблицы, установив столбцы на nullable, если вы не хотите, чтобы они были частью первичного ключа. Противоположное также верно, поскольку я столкнулся, сгенерированный EDM ключ вызывал проблемы с дублированием данных, поэтому мне пришлось определить столбец nullable как не nullable, чтобы заставить составной ключ в EDM для того чтобы включить этот столбец.
похоже, что это известная проблема с EdmGen: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/
чтобы получить представление, я должен был только показать один столбец первичного ключа я создал второе представление, которое указывало на первое и использовало NULLIF, чтобы сделать типы nullable. Это сработало для меня, чтобы заставить EF думать, что в представлении есть только один первичный ключ.
Не уверен, что это поможет вам, хотя, поскольку я не верю, что EF примет сущность без первичного ключа.
Я также рекомендую, если вы не хотите возиться с тем, что должно быть первичным ключом, чтобы включить ROW_NUMBER в свой выбор и установить его в качестве первичного ключа и установить все другие столбцы/члены как не первичные в модели.
из-за вышеупомянутых проблем я предпочитаю функции табличных значений.
Если у вас есть это:
CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something
создать этого:
CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])
тогда вы просто импортируете функцию, а не представление.