实体框架和 SQLServer 视图

由于以下几个原因,我不便谈论,我们正在 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

这个想法是,实体框架将创建一个基于这个查询的实体,它确实这样做了,但它生成了一个错误,指出以下内容:

警告6002: 表/视图‘ Keystone _ Local。Dbo.MeterProvingStatistics 点’没有定义主键。键已经推断出来,并且定义是作为只读表/视图创建的。

它决定 CompletedDateTime 字段将是这个实体主键。

我们正在使用 EdmGen 来生成模型。是否有一种方法不让实体框架包含此视图的任何字段作为主键?

114926 次浏览

当前的 Entity Framework EDM 生成器将从视图中的所有非空字段创建一个组合键。为了获得对此的控制,需要修改视图和基础表列,在不希望列成为主键的一部分时,将列设置为可为空。正如我所遇到的,情况恰恰相反,EDM 生成的键导致了数据复制问题,因此我必须将一个可为空的列定义为非空的,以强制 EDM 中的组合键包含该列。

为了获得一个视图,我只需要显示 主键列,我创建了第二个视图,它指向第一个视图,并使用 NULLIF 使类型可以为空。这使得 EF 认为视图中只有一个主键。

不确定这是否会帮助你,因为我不相信 EF 将接受一个没有主键的实体。

我们遇到了同样的问题,这就是解决办法:

若要强制实体框架使用列作为主键,请使用 ISNULL。

若要强制实体框架不使用列作为主键,请使用 NULLIF。

应用这一点的一种简单方法是将视图的 select 语句封装在另一个 select 中。

例如:

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

同意@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 优化器不会使用 index ix _ customer,它会对主索引执行表扫描,但是如果不使用:

Group by CustomerId

你用

Group by IsNull(CustomerId, -1)

这将使微软 SQL (至少2008年)纳入正确的索引计划。

如果

这个方法对我很有效。主键字段使用 ISNULL () ,如果该字段不应该是主键,但也应该具有非空值,则使用 COALESCE ()。此示例生成具有不可为空的主键的 ID 字段。其他字段不是键,并且有(Nothing)作为它们的 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

我通过设计师解决了这个问题。

  1. 打开模型浏览器。
  2. 在图表中查找视图。
  3. 右键单击主键,确保勾选了“实体键”。
  4. 多重选择所有非主键。使用 Ctrl 或 Shift 键。
  5. 在“属性”窗口中(如果需要查看,请按 F4) ,更改 “ Entity Key”下拉到 False。
  6. 保存更改。
  7. 关闭 VisualStudio 并重新打开它。我正在使用带有 EF6的 VisualStudio2013 我必须这么做才能让警告消失。

我不需要改变我的观点来使用 ISNULL、 NULLIF 或 COALESCE 变通方法。如果从数据库更新模型,警告将重新出现,但如果关闭并重新打开 VS,警告将消失。在设计器中所做的更改将保留,不受刷新的影响。

如果你不想搞乱什么应该是主键,我建议:

  1. ROW_NUMBER合并到您的选择中
  2. 设置为主键
  3. 将模型中的所有其他列/成员设置为非主列/成员

由于上述问题,我倾向于使用表值函数。

如果你有这个:

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

创造这个:

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

然后只需导入函数而不是视图。