web-dev-qa-db-fra.com

Entity Framework et vue SQL Server

Pour plusieurs raisons que je n'ai pas la liberté de parler, nous définissons une vue de notre base de données SQL Server 2005 comme suit:

CREATE VIEW [dbo].[MeterProvingStatisticsPoint]
AS
SELECT
    CAST(0 AS BIGINT) AS 'RowNumber',
    CAST(0 AS BIGINT) AS 'ProverTicketId',
    CAST(0 AS INT) AS 'ReportNumber',
    GETDATE() AS 'CompletedDateTime',
    CAST(1.1 AS float) AS 'MeterFactor',
    CAST(1.1 AS float) AS 'Density',
    CAST(1.1 AS float) AS 'FlowRate',
    CAST(1.1 AS float) AS 'Average',
    CAST(1.1 AS float) AS 'StandardDeviation',
    CAST(1.1 AS float) AS 'MeanPlus2XStandardDeviation',
    CAST(1.1 AS float) AS 'MeanMinus2XStandardDeviation'
WHERE 0 = 1

L'idée est que Entity Framework créera une entité sur la base de cette requête, ce qu'il fait, mais il la génère avec une erreur qui indique ce qui suit:

Avertissement 6002: aucune clé primaire n'a été définie dans la table/vue 'Keystone_Local.dbo.MeterProvingStatisticsPoint'. La clé a été déduite et la définition a été créée en tant que table/vue en lecture seule.

Et il décide que le champ CompletedDateTime sera la clé primaire de cette entité.

Nous utilisons EdmGen pour générer le modèle. Existe-t-il un moyen de ne pas laisser le cadre d'entité inclure un champ de cette vue en tant que clé primaire?

130
Sergio Romero

Nous avons eu le même problème et voici la solution:

Pour forcer le cadre de l'entité à utiliser une colonne en tant que clé primaire, utilisez ISNULL.

Pour forcer la structure d'entités à ne pas utiliser une colonne en tant que clé primaire, utilisez NULLIF.

Un moyen simple d'appliquer ceci consiste à encapsuler l'instruction select de votre vue dans une autre sélection.

Exemple:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp
237
Tillito

J'ai pu résoudre ce problème en utilisant le concepteur.

  1. Ouvrez le navigateur de modèle.
  2. Trouvez la vue dans le diagramme.
  3. Faites un clic droit sur la clé primaire et assurez-vous que "Clé de l'entité" est cochée.
  4. Sélectionner plusieurs fois toutes les clés non primaires. Utilisez les touches Ctrl ou Maj.
  5. Dans la fenêtre Propriétés (appuyez sur F4 si nécessaire pour le voir), définissez le menu déroulant "Clé de l'entité" sur False.
  6. Sauvegarder les modifications.
  7. Fermez Visual Studio et rouvrez-le. J'utilise Visual Studio 2013 avec EF 6 et je devais le faire pour que les avertissements disparaissent.

Je n'ai pas eu à changer d'avis pour utiliser les solutions de contournement ISNULL, NULLIF ou COALESCE. Si vous mettez à jour votre modèle à partir de la base de données, les avertissements réapparaîtront, mais disparaîtront si vous fermez et rouvrez VS. Les modifications apportées dans le concepteur seront préservées et ne seront pas affectées par l'actualisation.

65
Casey

D'accord avec @Tillito, cependant, dans la plupart des cas, l'optimiseur SQL sera utilisé sans utiliser les index corrects.

Cela peut sembler évident pour quelqu'un, mais j'ai passé des heures à résoudre des problèmes de performance avec la solution Tillito. Disons que vous avez la table:

 Create table OrderDetail
    (  
       Id int primary key,
       CustomerId int references Customer(Id),
       Amount decimal default(0)
    );
 Create index ix_customer on OrderDetail(CustomerId);

et votre vue est quelque chose comme ça

 Create view CustomerView
    As
      Select 
          IsNull(CustomerId, -1) as CustomerId, -- forcing EF to use it as key
          Sum(Amount) as Amount
      From OrderDetail
      Group by CustomerId

Sql optimizer n'utilisera pas index ix_customer et effectuera une analyse de la table sur l'index principal, mais si au lieu de:

Group by CustomerId

tu utilises

Group by IsNull(CustomerId, -1)

mS SQL (au moins 2008) inclura le bon index dans le plan.

Si

45
Val Bakhtin

Cette méthode fonctionne bien pour moi. J'utilise ISNULL () pour le champ de clé primaire et COALESCE () si le champ ne doit pas être la clé primaire, mais doit également avoir une valeur non nullable. Cet exemple renvoie un champ ID avec une clé primaire non Nullable. Les autres champs ne sont pas des clés et ont pour attribut Nullable (Aucun).

SELECT      
ISNULL(P.ID, - 1) AS ID,  
COALESCE (P.PurchaseAgent, U.[User Nickname]) AS PurchaseAgent,  
COALESCE (P.PurchaseAuthority, 0) AS PurchaseAuthority,  
COALESCE (P.AgencyCode, '') AS AgencyCode,  
COALESCE (P.UserID, U.ID) AS UserID,  
COALESCE (P.AssignPOs, 'false') AS AssignPOs,  
COALESCE (P.AuthString, '') AS AuthString,  
COALESCE (P.AssignVendors, 'false') AS AssignVendors 
FROM Users AS U  
INNER JOIN Users AS AU ON U.Login = AU.UserName  
LEFT OUTER JOIN PurchaseAgents AS P ON U.ID = P.UserID

si vous n'avez vraiment pas de clé primaire, vous pouvez en usurper une en utilisant ROW_NUMBER pour générer une pseudo-clé ignorée par votre code. Par exemple:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE
8
SpazDude

Le générateur EDM Entity Framework actuel créera une clé composite à partir de tous les champs non nullables de votre vue. Pour en prendre le contrôle, vous devez modifier les colonnes de la vue et de la table sous-jacentes en définissant les colonnes sur nullable lorsque vous ne souhaitez pas qu'elles fassent partie de la clé primaire. Le contraire est également vrai. La clé générée par EDM posait des problèmes de duplication de données. J'ai donc dû définir une colonne nullable comme non-nullable pour forcer la clé composite dans l'EDM à inclure cette colonne.

4
Annagram
3
RBarryYoung

Pour obtenir une vue, je ne devais montrer que n colonne de clé primaire, j'ai créé une deuxième vue qui pointait vers la première et utilisait NULLIF pour rendre les types nullable. Cela a permis à l'EF de penser qu'il n'y avait qu'une seule clé primaire dans la vue.

Je ne sais pas si cela vous aidera, car je ne crois pas que le FE acceptera une entité SANS clé primaire.

3
Nick Gotch

Je vous recommande également, si vous ne voulez pas jouer avec ce qui devrait être la clé primaire, d’incorporer ROW_NUMBER à votre sélection, de le définir comme clé primaire et de définir toutes les autres colonnes/membres comme non primaires dans le modèle.

2
Santhos

En raison des problèmes mentionnés ci-dessus, je préfère les fonctions de valeur de table.

Si vous avez ceci:

CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something

créer ceci:

CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])

Ensuite, vous importez simplement la fonction plutôt que la vue.

1
Ray