Je viens juste de commencer un projet de conversion d'une application brute ADO.NET et Embedded SQL en entité. J'ai rencontré un problème avec l'une des vues utilisées par l'application. La vue ne contient ni clé primaire ni colonne (ou combinaison de colonnes) identifiant de manière unique une ligne. Voici la sélection avec laquelle la vue est créée:
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;
Et voici quelques exemples de données montrant que certaines lignes sont complètement dupliquées (lignes 3 et 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
assuréid est le pk pour la table aip.slbtransinsured, le RSN est le pk pour aip.slbtransinsurer et aip.slblinecodes.
Est-il possible d'ajouter une vue au modèle Entity sans identifiant unique? Ou existe-t-il un moyen simple d’ajouter un identifiant de ligne unique à la vue? La vue est uniquement lue, jamais écrite.
Est-il possible d'ajouter une vue au modèle Entity sans un identifiant unique?
Si sans clé primaire, non. Cela entraînera ce genre de erreur :
Une ou plusieurs erreurs de validation ont été détectées lors de la génération du modèle:
System.Data.Edm.EdmEntityType:: Le type d'entité 'SalesOnEachCountry' a aucune clé définie. Définissez la clé pour ce EntityType . System.Data.Edm.EdmEntitySet: EntityType: EntitySet SalesOnEachCountryList est basé sur le type SalesOnEachCountry qui n'a pas clés définies.
Si sans identifiant unique, oui, bien qu'il ait une sortie non souhaitable. Les enregistrements avec le même identifiant référenceraient le même objet, cela s'appelle Identity Map Pattern
Un exemple, même si votre vue génère ces deux lignes:
Country Year TotalSales
Philippines 2010 20.000000
Philippines 2011 40.000000
Si vous voulez seulement mapper la clé primaire sur le champ Pays uniquement, par exemple
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; }
}
, même votre vue génère les deux lignes ci-dessus dans votre éditeur de requête Oracle, Entity Framework génère cette sortie incorrecte:
Country Year TotalSales
Philippines 2010 20.000000
Philippines 2010 20.000000
Entity Framework considérera que la deuxième ligne est le même objet que la première ligne.
Pour garantir l'unicité, vous devez identifier les colonnes qui rendent chaque ligne unique. Dans l'exemple ci-dessus, Year doit être inclus pour que la clé primaire soit unique. c'est à dire.
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; }
}
En rendant votre clé primaire similaire aux attributs ci-dessus, Entity Framework peut mapper correctement la ligne de chaque vue sur leurs propres objets. Par conséquent, Entity Framework peut désormais afficher exactement les mêmes lignes que votre vue.
Country Year TotalSales
Philippines 2010 20.000000
Philippines 2011 40.000000
Tous les détails ici: http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
Ensuite, en ce qui concerne vos vues qui ne possèdent aucune colonne pour rendre une ligne unique, le moyen le plus simple de garantir qu'Entity Framework mappe chaque ligne de votre vue sur ses propres objets consiste à créer une colonne distincte pour la clé primaire de votre vue. , un bon candidat consiste simplement à créer une colonne de numéro de ligne sur chaque ligne. par exemple.
create view RowNumberedView as
select
row_number() over(order by <columns of your view sorting>) as RN
, *
from your_existing_view
Attribuez ensuite l'attribut [Key]
sur la propriété RN de votre class RowNumberedView
Pour développer la réponse de Michael Buen: J'ai constaté que l'ajout du numéro de ligne à la vue avec un ISNULL () permettrait au framework d'entité d'extraire la vue et de créer automatiquement les données EntitySet nécessaires.
create view RowNumberedView as
select
ISNULL(ROW_NUMBER() OVER (ORDER BY <column>), 0) AS RN
, *
from your_existing_view
Un champ de compteur de lignes peut être ajouté à la vue pour l'utiliser comme clé.
S'il vous plaît voir lien .
Au travail récemment, j'ai rencontré le même problème. Sur la base de mes recherches, je n’ai trouvé aucune réponse sur la manière d’attacher une vue à EF6 CodeFirst sans PK. La plupart semblent impliquer des migrations et ont été assez déroutant. Je pense que DB a d’abord un meilleur support pour travailler avec SQL VIEWS
.
J'ai essayé d'introduire un window function
(RowNumber) avec l'idée suivante: utiliser l'identificateur de ligne comme PK pour garder EF6 heureux. Mais, dans l’ensemble, ma requête a été plus coûteuse et j’ai donc dû abandonner cette idée.
Finalement, j'ai dû analyser soigneusement mon ensemble de données pour voir si je pouvais introduire une clé composite - une clé qui couvre tous les scénarios que mon application métier devait assurer pour fonctionner. N'oubliez pas d'utiliser également IsNull(ColumnName,0)
pour vous assurer que vous pouvez satisfaire à la méthode .IsRequired()
dans les méthodes fluides CodeFirst.
c'est à dire
HasKey(x => new { x.KfiId, x.ApplicationNumber, x.CustomerId });
J'espère que cela aide quelqu'un - la solution pour moi était d'analyser l'ensemble de données que la vue dénormalise et de rechercher une clé composite.
Une autre bonne idée que vous pouvez essayer suggérée par Marc Cals .
Est-il possible d'ajouter une vue au modèle Entity sans identifiant unique?
Il est possible d'avoir une vue où il n'y a pas une seule colonne ou un ensemble de colonnes qui crée la clé primaire; ainsi, vous vous retrouvez avec de fausses relations. Les tables d'entrepôt de données suivent parfois ce formulaire. En bref, la normalisation n'est parfois pas suivie pour des raisons de performances ou de génération de rapports.
Passons maintenant à votre deuxième point:
Ou existe-t-il un moyen simple d’ajouter un identifiant de ligne unique à la vue?
Ce que je vous suggère de faire est de sélectionner toutes les colonnes de slbtransansured et de voir si vous pouvez trouver la colonne qui identifie de manière unique chaque enregistrement. Il me semble que les données sont il devrait y avoir un type de code dans slblinecodes que vous devez sélectionner, un peu comme une recherche.
Pour les coups de pied, essayez ceci et dites-moi ce que vous obtenez:
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;