使用没有主键的视图与实体

时间:2012-07-06 22:02:41

标签: sql oracle entity

我刚开始将一个应用程序从原始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。

是否可以在没有唯一标识符的情况下向实体模型添加视图?或者是否有一种简单的方法可以向视图添加唯一的行标识符?该视图仅从中读取,从不写入。

6 个答案:

答案 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]

的RN属性上分配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)

如果您在ASP.NET中将实体框架与MVC一起使用

如前所述,使用具有自动增量或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