我刚开始将一个应用程序从原始ADO.NET和嵌入式SQL转换为Entity。我遇到了应用程序使用的其中一个视图的问题。视图没有主键,也没有唯一标识行的列(或列组合)。以下是使用以下选项创建视图:
SELECT
filingmonth,
CEIL(filingmonth / 3),
licnum,
filingyear,
DECODE(GROUPING(insurername), '1', '- All Insured -', insurername),
insurername,
policylinecode,
linedescription,
SUM(NVL(grosspremium, 0)),
SUM(DECODE(taxexempt, 1, grosspremium, 0)),
TRUNC(
CASE
WHEN
(
b.rsn IS NOT NULL
OR A.zeroreport = 1
)
AND b.datereceived IS NULL
THEN A.datereceived
ELSE b.datereceived
END),
SUM(aip.iscompanyadmitted(b.naiccocode, b.naicalienid)),
A.insuredid
FROM
aip.slbtransinsured A
LEFT OUTER JOIN aip.slbtransinsurer b
ON
A.insuredid = b.insuredid
LEFT OUTER JOIN aip.slblinecodes C
ON
b.policylinecode = C.linecode
WHERE
A.submitted = 1
AND A.entryincomplete = 0
GROUP BY
licnum,
filingmonth,
filingyear,
TRUNC(
CASE
WHEN
(
b.rsn IS NOT NULL
OR A.zeroreport = 1
)
AND b.datereceived IS NULL
THEN A.datereceived
ELSE b.datereceived
END),
ROLLUP(insurername, aip.iscompanyadmitted(b.naiccocode, b.naicalienid),
policylinecode, linedescription), A.insuredid;
这里有一些示例数据显示有些行完全重复(第3行和第4行):
FILINGMONTH CEIL(FILINGMONTH/3) LICNUM FILINGYEAR DECODE(GROUPING(INSURERNAME),'1','-ALLINSURED-',INSURERNAME) INSURERNAME POLICYLINECODE LINEDESCRIPTION SUM(NVL(GROSSPREMIUM,0)) SUM(DECODE(TAXEXEMPT,1,GROSSPREMIUM,0)) TRUNC(CASEWHEN(B.RSNISNOTNULLORA.ZEROREPORT=1)ANDB.DATERECEIVEDISNULLTHENA.DATERECEIVEDELSEB.DATERECEIVEDEND) SUM(AIP.ISCOMPANYADMITTED(B.NAICCOCODE,B.NAICALIENID)) INSUREDID
6 2 8150 2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO SAVERS PROPERTY AND CASUALTY INSURANCE CO 17 OTHER LIABILITY 721.25 0 18-JUL-07 0 81
6 2 8150 2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO SAVERS PROPERTY AND CASUALTY INSURANCE CO 17 721.25 0 18-JUL-07 0 81
6 2 8150 2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO SAVERS PROPERTY AND CASUALTY INSURANCE CO 721.25 0 18-JUL-07 0 81
6 2 8150 2007 SAVERS PROPERTY AND CASUALTY INSURANCE CO SAVERS PROPERTY AND CASUALTY INSURANCE CO 721.25 0 18-JUL-07 0 81
insuredid是aip.slbtransinsured表的pk,rsn是aip.slbtransinsurer和aip.slblinecodes的pk。
是否可以在没有唯一标识符的情况下向实体模型添加视图?或者是否有一种简单的方法可以向视图添加唯一的行标识符?该视图仅从中读取,从不写入。
答案 0 :(得分:31)
是否可以在没有a的情况下向Entity模型添加视图 唯一标识符?
如果没有主键,没有。这将产生这种error:
在模型生成期间检测到一个或多个验证错误:
System.Data.Edm.EdmEntityType :: EntityType'SalesOnEachCountry'具有 没有键定义。定义此EntityType的键。 System.Data.Edm.EdmEntitySet:EntityType:EntitySet SalesOnEachCountryList基于没有的SalesOnEachCountry类型 键定义。
如果没有唯一标识符,是的,尽管它具有非期望的输出。具有相同标识符的记录将引用同一对象,这称为Identity Map Pattern
一个例子,即使你的视图产生这两行:
Country Year TotalSales
Philippines 2010 20.000000
Philippines 2011 40.000000
如果您只是在国家/地区字段上映射主键,例如
public class SalesOnEachCountry
{
[Key]
public int CountryId { get; set; }
public string CountryName { get; set; }
public int OrYear { get; set; }
public long SalesCount { get; set; }
public decimal TotalSales { get; set; }
}
,即使您的视图在Oracle查询编辑器上生成上述两行,Entity Framework也会生成错误的输出:
Country Year TotalSales
Philippines 2010 20.000000
Philippines 2010 20.000000
实体框架会认为第二行与第一行是同一个对象。
为了保证唯一性,您必须确定使每行唯一的列。在上面的示例中,必须包含Year,因此主键是唯一的。即
public class SalesOnEachCountry
{
[Key, Column(Order=0)] public int CountryId { get; set; }
public string CountryName { get; set; }
[Key, Column(Order=1)] public int OrYear { get; set; }
public long SalesCount { get; set; }
public decimal TotalSales { get; set; }
}
使主键与上述属性类似,Entity Framework可以正确地将每个视图的行映射到它们自己的对象。因此,实体框架现在可以显示与视图完全相同的行。
Country Year TotalSales
Philippines 2010 20.000000
Philippines 2011 40.000000
此处详细信息:http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
然后,对于没有任何列来创建行唯一的视图,保证实体框架可以将每个视图的行映射到它们自己的对象的最简单方法是为视图的创建单独的列主键,一个好的候选者就是在每一行上创建一个行号列。 e.g。
create view RowNumberedView as
select
row_number() over(order by <columns of your view sorting>) as RN
, *
from your_existing_view
然后在[Key]
class RowNumberedView
属性
答案 1 :(得分:15)
扩展Michael Buen的答案: 我发现使用ISNULL()将行号添加到视图中将允许实体框架拉入视图并自动创建必要的EntitySet数据。
pro-sip*CLI> core show function RAND
-= Info about function 'RAND' =-
[Synopsis]
Choose a random number in a range.
[Description]
Choose a random number between <min> and <max>. <min> defaults to '0', if
not specified, while <max> defaults to 'RAND_MAX' (2147483647 on many
systems).
Example: Set(junky=${RAND(1,8)}); Sets junky to a random number between
1 and 8, inclusive.
[Syntax]
RAND([min][,max])
[Arguments]
Not available
[See Also]
Not available
答案 2 :(得分:1)
最近在工作中,我遇到了同样的问题。基于我的研究,我找不到任何关于如何在没有PK的情况下将视图附加到EF6 CodeFirst的答案。大多数似乎都涉及迁移,而且非常令人困惑。我相信DB首先会更好地支持SQL VIEWS
。
我尝试引入一个window function
(RowNumber)的想法是,使用行标识符作为PK来保持EF6 Happy。但这使我的查询总体上更加昂贵,所以我不得不放弃这个想法。
最后,我必须仔细分析我的数据集,看看我是否可以引入一个复合键 - 一个涵盖我的业务应用程序确保工作所需的所有场景。请务必使用IsNull(ColumnName,0)
,以确保您能够满足CodeFirst流畅方法中的.IsRequired()
。
即
HasKey(x => new { x.KfiId, x.ApplicationNumber, x.CustomerId });
我希望这对某人有所帮助 - 对我来说,答案是分析视图非规范化的数据集并寻找复合键。
Marc Cals建议的另一个很酷的想法。
答案 3 :(得分:1)
如前所述,使用具有自动增量或ROW_NUMBER的列创建视图。
假设您有该列,名称为rowNumber
。
然后转到MVC应用程序的yourDatabaseNameContext
目录中的上下文文件(Models
)文件,查找视图的定义,而不是
modelBuilder.Entity<yourView>(entity =>
{
entity.HasNoKey();
将其更改为:
modelBuilder.Entity<yourView>(entity =>
{
entity.HasKey(e => e.rowNumber);
答案 4 :(得分:0)
是否可以在没有唯一标识符的情况下向实体模型添加视图?
可以有一个视图,其中没有一列或一组列创建主键;因此,你最终会陷入虚假的关系。数据仓库表有时遵循该表单。简而言之,出于性能原因或报告原因,有时不遵循规范化。
现在到了第二点:
或者有一种简单的方法可以在视图中添加唯一的行标识符吗?
我建议您选择slbtransinsured中的所有列,看看是否可以找到唯一标识每条记录的列。在我看来,数据应该是slblinecodes中需要选择的代码类型,有点像查找。
对于踢球,尝试运行它并告诉我你得到了什么:
SELECT filingmonth,
CEIL (filingmonth / 3),
licnum,
filingyear,
DECODE (GROUPING (insurername), '1', '- All Insured -', insurername),
insurername,
policylinecode,
linedescription,
SUM (NVL (grosspremium, 0)),
SUM (DECODE (taxexempt, 1, grosspremium, 0)),
TRUNC (
CASE
WHEN (b.rsn IS NOT NULL OR a.zeroreport = 1)
AND b.datereceived IS NULL
THEN
a.datereceived
ELSE
b.datereceived
END),
SUM (aip.iscompanyadmitted (b.naiccocode, b.naicalienid)),
a.insuredid
FROM aip.slbtransinsured a
LEFT OUTER JOIN aip.slbtransinsurer b
ON a.insuredid = b.insuredid
LEFT OUTER JOIN aip.slblinecodes c
ON b.policylinecode = c.linecode
WHERE a.submitted = 1 AND a.entryincomplete = 0
GROUP BY filingmonth,
licnum,
filingyear,
DECODE (GROUPING (insurername), '1', '- All Insured -', insurername),
insurername,
policylinecode,
linedescription,
TRUNC (
CASE
WHEN (b.rsn IS NOT NULL OR a.zeroreport = 1)
AND b.datereceived IS NULL
THEN
a.datereceived
ELSE
b.datereceived
END),
a.insuredid;
答案 5 :(得分:0)
可以在视图中添加行计数器字段以用作密钥。
请参阅link。