实体框架和 SQL Server 视图
Posted
技术标签:
【中文标题】实体框架和 SQL Server 视图【英文标题】:Entity Framework and SQL Server View 【发布时间】:2010-11-04 01:00:06 【问题描述】:出于几个我无权谈论的原因,我们在 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.MeterProvingStatisticsPoint”没有定义主键。已推断出密钥,并将定义创建为只读表/视图。
它决定 CompletedDateTime 字段将是这个实体的主键。
我们正在使用 EdmGen 生成模型。有没有办法不让实体框架包含这个视图的任何字段作为主键?
【问题讨论】:
【参考方案1】:看起来这是 EdmGen 的一个已知问题:http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/
【讨论】:
这是有道理的。那么,有没有办法像我们定义的那样在视图中将列定义为非空或空? 对不起,我已经超出了我在实体框架方面的专业水平。 :-) 有人知道这个问题什么时候能解决吗?当您有非主键的非空列时,不得不解决这个问题很烦人。【参考方案2】:当前的实体框架 EDM 生成器将从您视图中的所有不可为空的字段创建一个复合键。为了对此进行控制,当您不希望它们成为主键的一部分时,您需要修改视图和基础表列,将这些列设置为可为空。反之亦然,正如我所遇到的,EDM 生成的键会导致数据重复问题,因此我必须将可空列定义为不可空,以强制 EDM 中的复合键包含该列。
【讨论】:
我们对推断的 PK 也有同样的问题,实体返回重复记录,非常烦人。如果执行Context.Entity.ToList()
会重复记录,但如果直接执行EF生成的SQL Query(使用LINQPad获取),则不会发生重复记录。将数据库记录映射到返回的实体对象(POCO)似乎是一个问题,因为 PK 是使用解释的逻辑(不可为空的列)推断出来的。【参考方案3】:
为了获得一个视图,我只需要显示 一个 主键列,我创建了第二个视图,它指向第一个视图并使用 NULLIF 使类型可以为空。这对我有用,让 EF 认为视图中只有一个主键。
不确定这是否会对您有所帮助,因为我不相信 EF 会接受没有主键的实体。
【讨论】:
【参考方案4】:我们遇到了同样的问题,这就是解决方案:
要强制实体框架使用列作为主键,请使用 ISNULL。
要强制实体框架不使用列作为主键,请使用 NULLIF。
一个简单的应用方法是将视图的 select 语句包装在另一个 select 中。
例子:
SELECT
ISNULL(MyPrimaryID,-999) MyPrimaryID,
NULLIF(AnotherProperty,'') AnotherProperty
FROM ( ... ) AS temp
【讨论】:
我认为这是最好的希望。底线是有效的。 谢谢!它工作得很好。 @sabanito 我认为它解析了定义。这就是为什么您需要在 IsNull() 中专门包装关键属性的原因。我有一个不返回任何空值(并且不能返回任何空值)的视图,但是由于逻辑的编写方式,EF 无法确定是这种情况,直到我将键包装在 IsNull() 中。 我在这里看到的唯一问题是视图可能合法地需要返回一个空字符串''。我所做的只是将列转换回它自己的数据类型。例如,如果 AnotherProperty 的数据类型为 varchar(50),我会将其转换为“CONVERT(VARCHAR(50), AnotherProperty) AS [AnotherProperty]”。这掩盖了 EF 的可空性,也允许空字符串。 是的,例如,这可以使 EF 将该列用作主键 isnull(CONVERT(VARCHAR(50), newid()),'') AS [PK] 除了解决方案中只有一条烦人的消息之外,不解决这个问题有什么害处吗?我同意您的解决方案,但坦率地说,我认为我不应该这样做 - 我认为我们都同意这是一个错误,对吧?【参考方案5】:同意@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 年)在计划中包含正确的索引。
如果
【讨论】:
这应该是对 Tillito 答案的评论,而不是答案本身,因为它没有为 OP 的问题提供解决方案。 这个人的代表是 1,他还不能添加评论。 @zimdanen 您无法将所有这些信息放入评论中,将其放在单独的答案中更有意义。 @Contango:这个答案是在发布六天后编辑的,我发表了我的评论。查看修订历史记录。【参考方案6】:这种方法对我很有效。我对主键字段使用 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,丑。【参考方案7】:
我能够使用设计器解决此问题。
-
打开模型浏览器。
在图中找到视图。
右键单击主键,并确保选中“实体键”。
多选所有非主键。使用 Ctrl 或 Shift 键。
在“属性”窗口中(如果需要,请按 F4 键),更改
“实体键”下拉菜单为 False。
保存更改。
关闭 Visual Studio 并重新打开它。我正在使用带有 EF 6 的 Visual Studio 2013
我必须这样做才能让警告消失。
我不必改变我的观点来使用 ISNULL、NULLIF 或 COALESCE 变通办法。如果您从数据库更新模型,警告将重新出现,但如果您关闭并重新打开 VS,警告就会消失。您在设计器中所做的更改将被保留,不受刷新影响。
【讨论】:
已确认。必须重新启动 VS2013 才能使警告消失。 "您是否尝试将其关闭再打开?" ;-) 谢谢,就像一个魅力! 当我创建视图时,它们甚至没有出现在模型图中。它们在 xml 文件中被注释 简单易行的解决方案,看起来不像操纵视图那样是一个非常棘手的修复!谢谢。 确认 VS2017 也需要重新启动,警告才会消失。【参考方案8】:如果您不想弄乱主键,我建议:
-
将
ROW_NUMBER
合并到您的选择中
设置为主键
在模型中将所有其他列/成员设置为非主要的
【讨论】:
【参考方案9】:由于上述问题,我更喜欢表值函数。
如果你有这个:
CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something
创建这个:
CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])
然后您只需导入函数而不是视图。
【讨论】:
您将如何在采用这种方法的实体之间创建关联?有可能吗?以上是关于实体框架和 SQL Server 视图的主要内容,如果未能解决你的问题,请参考以下文章
带有实体框架和 SQL Server 数据库的 ASP.NET MVC - 图像未显示在视图中......错误显示“无法将“字节”转换为“字符串”