I just started on a project converting an application from raw ADO.NET and embedded SQL to Entity. I ran in to a problem with one of the views used by the application. The view has no primary key and no column (or combination of columns) that uniquely identifies a row. Here is the select the view is created with:
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;
And here is some sample data showing that there are some rows that are completely duplicated (rows 3 and 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 is the pk for the aip.slbtransinsured table, rsn is the pk for aip.slbtransinsurer and aip.slblinecodes.
Is it at all possible to add a view to the Entity model without a unique identifier? Or is there an easy way to add a unique row identifier to the view? The view is only read from, never written to.
There is, however, a way to get a view added as a "read only entity" sans primary key if you use database-first Entity Framework. You even get a neat error when you add a view without an obvious key that says EF is forcing your entity to be a keyless read-only entity.
The Entity framework will not support to have a table without primary key, but we can overcome this issue by accessing the table with additional column via a view and marking the new column as Primary in entity framework. Entity Framework requires primary keys for entities.
Every table can have (but does not have to have) a primary key. The column or columns defined as the primary key ensure uniqueness in the table; no two rows can have the same key. The primary key of one table may also help to identify records in other tables, and be part of the second table's primary key.
Popular Answer There is a great difference between what EF can do with a database, and what is possible with a database. Most databases allow for a table to be without a primary key.
Is it at all possible to add a view to the Entity model without a unique identifier?
If without a primary key, no. That will result to this kind of error:
One or more validation errors were detected during model generation:
System.Data.Edm.EdmEntityType: : EntityType 'SalesOnEachCountry' has no key defined. Define the key for this EntityType. System.Data.Edm.EdmEntitySet: EntityType: The EntitySet SalesOnEachCountryList is based on type SalesOnEachCountry that has no keys defined.
If without a unique identifier, yes, albeit it has a non-desirable output. Records with same identifier would reference the same object, this is called Identity Map Pattern
An example, even if your view produces these two rows:
Country Year TotalSales Philippines 2010 20.000000 Philippines 2011 40.000000
If you will just map the primary key on Country field only, e.g.
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; } }
, even your view produces the above two rows on your Oracle query editor, Entity Framework produces this incorrect output:
Country Year TotalSales Philippines 2010 20.000000 Philippines 2010 20.000000
Entity Framework will take it that the second row is same object as first row.
To guarantee uniqueness, you must identify what columns that makes each row unique. In the above example, Year must be included so the primary key is unique. i.e.
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; } }
Making your primary key similar to the attributes above, Entity Framework can correctly map your each view's row to their own objects. Hence, Entity Framework can now display exactly the same rows your view have.
Country Year TotalSales Philippines 2010 20.000000 Philippines 2011 40.000000
Full details here: http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
Then regarding your views which don't have any columns to make a row unique, the easiest way to guarantee Entity Framework can map each of your view's row to their own objects is to create a separate column for your view's primary key, a good candidate is to just create a row number column on each row. e.g.
create view RowNumberedView as select row_number() over(order by <columns of your view sorting>) as RN , * from your_existing_view
Then assign the [Key]
attribute on RN property of your class RowNumberedView
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With