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?
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
J'ai pu résoudre ce problème en utilisant le concepteur.
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.
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
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
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.
On dirait que c'est un problème connu avec EdmGen: http://social.msdn.Microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c959585945/
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.
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.
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.