实体框架和SQL Server视图


132

由于我没有自由谈论的几个原因,我们在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生成模型。有没有一种方法可以使实体框架不包含此视图的任何字段作为主键?

Answers:


245

我们遇到了同样的问题,这是解决方案:

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

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

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

例:

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

2
我认为这是最好的希望。底线是可行的。
MvcCmsJon 2010年

1
谢谢!效果很好。@sabanito我认为它解析了定义。这就是为什么您需要将关键属性特别包装在IsNull()中的原因。我有一个视图,它不返回任何空值(也不能返回任何空值),但是由于编写逻辑的方式,EF在我将键包装在IsNull()中之前无法确定情况是否如此。
拉比

3
我在这里看到的唯一问题是视图可能合法地需要返回空字符串”。我所做的只是将列转换回自己的数据类型。例如,如果AnotherProperty的数据类型为varchar(50),则将其强制转换为“ CONVERT(VARCHAR(50),AnotherProperty)AS [AnotherProperty]”。这掩盖了EF的可空性,并且还允许使用空字符串。
巴特2012年

2
是的,例如,可以使EF将列用作主键isull(CONVERT(VARCHAR(50),newid()),'')AS [PK]
dc2009

2
除了解决方案中只有令人讨厌的消息之外,不解决此问题是否有任何危害?我同意您的解决方案,但坦率地说,我认为我不必这样做-我认为我们都可以同意这是一个错误,对吗?
dyslexicanaboko

67

我能够使用设计器解决此问题。

  1. 打开模型浏览器。
  2. 在图中找到视图。
  3. 右键单击主键,并确保已选中“实体键”。
  4. 多选所有非主键。使用Ctrl或Shift键。
  5. 在“属性”窗口中(如果需要,请按F4键),将“实体键”下拉列表更改为False。
  6. 保存更改。
  7. 关闭Visual Studio,然后重新打开它。我将Visual Studio 2013与EF 6配合使用,我必须这样做才能使警告消失。

我不必更改视图即可使用ISNULL,NULLIF或COALESCE解决方法。如果从数据库更新模型,警告将再次出现,但是如果关闭并重新打开VS,警告将消失。您在设计器中所做的更改将被保留,并且不受刷新的影响。


9
已确认。必须重新启动VS2013才能使警告消失。
Michael Logutov 2014年

5
“你试过把它关掉再打开吗?” ;-)谢谢,作品就像一个魅力!
Obl Tobl 2014年

4
当我创建视图时,它们甚至都不会出现在模型图中。他们在xml文件中被注释
ggderas

简单易用的解决方案,似乎并没有像操纵视图那样具有非凡的解决方案!谢谢。
LuqJensen '16

2
确认的VS2017也需要重新启动才能消除警告。
马克·莱夫斯克

46

同意@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年)将正确的索引纳入计划。

如果


2
这应该是对Tillito答案的评论,而不是答案本身,因为它不能为OP的问题提供解决方案。
zimdanen 2012年

6
该人的代表为1,他无法添加评论。
jrcs3 2012年

@zimdanen不可能将所有这些信息都放入注释中,将其包含在单独的答案中更有意义。
康坦戈2014年

2
@Contango:此答案在发布后六天进行了编辑,我发表了评论。查看修订历史。
zimdanen 2014年

9

这种方法对我来说很好。我将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,丑。
鲁芬

4

当前的Entity Framework EDM生成器将根据视图中的所有非空字段创建一个组合键。为了对此进行控制,当您不希望将视图列和基础表列作为主键的一部分时,需要将其设置为可为空,从而将视图和基础表列设置为可空。反之亦然,正如我所遇到的那样,EDM生成的键导致了数据重复问题,因此我不得不将可为空的列定义为不可为空,以强制EDM中的复合键包括该列。


推断的PK存在相同的问题,该实体返回重复的记录并且完全令人讨厌。如果执行Context.Entity.ToList()重复记录,但是如果直接执行EF生成的SQL查询(使用LINQPad获取),则不会发生记录重复。似乎是将数据库记录映射到返回的实体对象(PO​​CO)时遇到的问题,因为使用说明的逻辑(不可为空的列)推断PK。
DavidOlivánUbieto '15

3

看来这是EdmGen的一个已知问题:http ://social.msdn.microsoft.com/forums/zh-CN/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/


这就说得通了。那么,有没有一种方法可以像我们定义列的方式那样将列定义为非null或null?
瑟吉奥·罗梅罗

1
抱歉,我已经超出了实体框架的专业水平。:-)
RBarryYoung 2009年

1
谁知道这个问题什么时候才能解决?当您拥有非主键的非null列时,必须解决此问题,这很烦人。
现场直播

3

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

不确定是否对您有帮助,因为我不相信EF会接受没有主键的实体。


3

如果您不想弄乱应该作为主键的内容,我建议:

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

1

由于上述问题,我更喜欢表值函数。

如果您有这个:

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

创建这个:

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

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


2
您将如何在采用这种方法的实体之间创建关联?可能吗?
ggderas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.