web-dev-qa-db-fra.com

Utilisation d'une vue sans clé primaire avec Entity

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.

20
Mike B

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

30
Michael Buen

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
15
armstb01

Un champ de compteur de lignes peut être ajouté à la vue pour l'utiliser comme clé.

S'il vous plaît voir lien .

0
crokusek

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 .

0
IbrarMumtaz

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;
0
Roberto Navarro