web-dev-qa-db-fra.com

La procédure stockée renvoie int au lieu du jeu de résultats

J'ai une procédure stockée qui contient la sélection dynamique. Quelque chose comme ça:

ALTER PROCEDURE [dbo].[usp_GetTestRecords] 
    --@p1 int = 0, 
    --@p2 int = 0
    @groupId nvarchar(10) = 0
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @query  NVARCHAR(max)

    SET @query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + @groupId
    /* This actually contains a dynamic pivot select statement */

    EXECUTE(@query);
END

Dans SSMS, la procédure stockée fonctionne correctement et affiche le jeu de résultats.

En C #, en utilisant Entity Framework, il montre le retour d'une int au lieu de IEnumerable?

private void LoadTestRecords()
{
    TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
    string id = ddlGroupId.SelectedValue;

    List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
    GridView1.DataSource = list;
}

Fonction générée pour usp_GetTestRecords

public virtual int usp_GetTestRecords(string groupId)
{
    var groupIdParameter = groupId != null ?
        new ObjectParameter("groupId", groupId) :
        new ObjectParameter("groupId", typeof(string));

    return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
23
Rod

Entity Framework ne peut pas dire ce que votre procédure stockée renvoie. J'ai réussi à créer une variable de table qui reflète les données de votre instruction SELECT. Insérez simplement dans la variable de table puis faites une sélection à partir de cette variable de table. EF devrait le prendre.

13
Rob

Je l'obtiens quand j'ai une procédure stockée qui inclut un appel "exec" dans une table temporaire, telle que:

insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0

Il semble qu'Entity Framework s'embrouille quant à ce qui devrait être renvoyé par la procédure. La solution que j'ai rencontrée consiste à ajouter ceci au sommet du sproc:

SET FMTONLY OFF

Après cela, tout va bien.

19
Yelnic

J'ai eu le même problème, et trouvé la solution ici

  1. Déplacer vers votre .edmx
  2. Dans la fenêtre/les fonctions du navigateur de modèles, trouvez votre procédure, puis double-cliquez dessus.
  3. Changer le type de retour que vous voulez
  4. Enregistrez .edmx et vérifiez à nouveau le type de retour.

 Screenshot

Ce devrait être ce dont vous avez besoin maintenant.

16
蕭為元

Voir la réponse de Ladislav Mrnka dans cet article Stool Overflow https://stackoverflow.com/a/7131344/4318324

J'ai eu le même problème de base.

Ajouter 

SET FMTONLY OFF

Le problème que vous tentez d’importer lors de l’importation au cours de l’importation est corrigé ..____ Il est recommandé de supprimer la ligne ultérieurement, sauf si la base de données a pour seul but de fournir un schéma pour EF (Entity Framework).

La principale raison de prudence est que EF utilise ce paramètre pour empêcher les mutations de données lors de la tentative d'obtention de métadonnées.

Si vous actualisez votre modèle d'entité à partir d'une base de données, toute procédure contenant cette ligne peut potentiellement mettre à jour les données de cette base simplement en essayant d'obtenir le schéma.

Je voulais ajouter une note supplémentaire à ce sujet afin qu'il ne soit pas nécessaire de parcourir intégralement l'autre lien.

si vous voulez essayer d’utiliser FMTONLY, voici quelques points à garder à l’esprit.

 lorsque FMTONLY est activé: 
 1) seul le schéma est renvoyé (non) lignes .
 similaire à l'ajout d'une déclaration fausse générale à votre clause where (c'est-à-dire "où 1 = 0") 
 2) les instructions de contrôle de flux sont ignorées 

Exemple

 mis en permanence sur 

 si 1 = 1 
 début 
 sélectionnez 1 a 
 end 
 else 
 begin 
 sélectionnez 1 a, 2 b 
 end 

 tandis que 1 = 1 
 sélectionnez 1 c 

Ce qui précède ne renvoie aucune ligne et les métadonnées de chacune des trois requêtes.

Pour cette raison, certaines personnes suggèrent d’en changer en tirant parti du fait qu’il ne respecte pas le contrôle de flux.

 si 1 = 0 
 début 
 mettre finement à off 
 end 

En fait, vous pouvez utiliser ceci pour introduire une logique qui suit cette

 réglé fmtonly off 
 declare @g varchar (30) 
 set @g = 'fmtonly a été désactivé' 

 si 1 = 0 
 début 
 régler fmtonly off 
 set @g = 'fmtonly était sur on' 
 end 

 sélectionnez @g 

Réfléchissez très attentivement avant d'essayer d'utiliser cette fonctionnalité, car elle est à la fois déconseillée et rend potentiellement extrêmement difficile le suivi de SQL

les principaux concepts à comprendre sont les suivants:

1. EF active FMTONLY pour empêcher la MUTATION de données d’exécuter des procédures stockées 
 quand il les exécute pendant la mise à jour du modèle .
 (dont il découle) 

 2. en désactivant FMTONLY dans toute procédure par laquelle EF essaiera de faire une analyse de schéma 
 (potentiellement ANY et EACHONE) introduit le potentiel de mutation de la base de données 
 les données chaque fois que quiconque tente de mettre à jour son modèle de base de données .
6
wode

Entity Framework renverra automatiquement une valeur scalaire si votre procédure stockée n'a pas de clé primaire dans votre jeu de résultats. Ainsi, vous devez inclure une colonne de clé primaire dans votre instruction select ou créer une table temporaire avec une clé primaire afin qu'Entity Framework renvoie un ensemble de résultats pour votre procédure stockée.

4
hoangvu

J'ai eu le même problème, j'ai changé le nom des champs de retour par le mot clé 'AS' et j'ai résolu mon problème. Une des raisons de ce problème est l'attribution de noms aux noms de colonnes avec des mots clés réservés SQL Server.

L'exemple est jachères:

ALTER PROCEDURE [dbo].[usp_GetProducts]

AS
BEGIN

 SET NOCOUNT ON;

 SELECT 
      , p.Id
      , p.Title
      , p.Description AS 'Description'

    FROM dbo.Products AS p
END
1
Armen Hovsepian

Lorsque vous avez généré votre classe de modèle pour votre procédure stockée, vous avez choisi le résultat du retour scalaire par erreur. vous devez supprimer votre procédure stockée de votre modèle d'entité, puis rajouter la procédure stockée. Dans la boîte de dialogue de la procédure stockée, vous pouvez choisir le type de retour attendu.Ne paséditez simplement le code généré. Cela peut fonctionner maintenant, mais le code généré peut être remplacé si vous apportez d'autres modifications à votre modèle.

1
Claies

J'ai réfléchi un peu à cela et je pense avoir une réponse meilleure/plus simple

Si vous avez un complexe stocké qui rend difficile la structure d'entités (pour les versions actuelles d'Entity Framework qui utilisent la balise FMTONLY pour acquérir le schéma)

pensez à faire ce qui suit au début de votre procédure stockée.

-- où [columnlist] correspond au schéma que vous souhaitez que EF récupère pour votre procédure stockée 

 si 1 = 0 
 begin 
 sélectionnez 
 [columnlist] 
 de [liste de tables et jointures] .__ où 1 = 0 
 fin 

si vous êtes en mesure de charger votre jeu de résultats dans une variable de tablevous pouvez procéder comme suit pour que votre schéma reste synchronisé

 déclarer @tablevar comme table 
 (
 blah int 
, moreblah varchar (20) 
) 

 si 1 = 0 
 début 
 sélectionnez * dans @tablevar 
 end 

...
-- chargez des données dans @tablevar 
 sélectionnez * dans @tablevar 
1
wode

Eh bien, j’avais ce problème aussi, mais après des heures de recherche en ligne, aucune des méthodes ci-dessus n’a aidé… .. Enfin, j’ai appris que cela se produirait si votre procédure de magasin obtenait des paramètres aussi nuls et générant des erreurs d’exécution des requêtes. Entity Framework générera une méthode pour la procédure de magasin en définissant le modèle d’entité complexe. En raison de cette valeur nulle, votre procédure de magasin renverra une valeur int. 

Veuillez vérifier votre procédure de stockage soit en fournissant des résultats vides avec des valeurs null. Cela résoudra votre problème. J'espère.

0
Murxiii Javed

Si vous avez besoin de faire cela, alors vous feriez peut-être mieux de faire une partie du dbcontext et de créer vous-même la fonction C # qui utilisera SqlQuery pour retourner les données dont vous avez besoin. Les avantages par rapport aux autres options sont les suivants:

  1. Ne rien changer lors de la mise à jour du modèle
  2. Ne sera pas écrasé si vous le faites directement dans la classe générée (quelqu'un ci-dessus le mentionne comme s'il s'agissait d'une option :))
  3. Ne pas avoir à ajouter quoi que ce soit au proc qui pourrait avoir des effets secondaires maintenant ou plus tard

Exemple de code:

 public partial class myEntities
    {
       public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
       {
          // fill out params
          SqlParameter p1 = new SqlParameter("@p1", _p1);
          ...
          obj[] parameters = new object[] { p1, p2, groupId };

          // call the proc
          return this.Database.SqlQuery<MyClass>(@"EXECUTE usp_GetTestRecords @p1, @p2, @groupId", parameters).ToList();
       }
    }
0
user441521

La meilleure solution que j'ai trouvée est de tricher un peu.

Dans la procédure de magasin, commentez tout, mettez une première ligne avec un select [foo]='', [bar]='', etc., mettez à jour le modèle, allez à la fonction mappée, sélectionnez le type complexe et cliquez sur Get Column Information, puis Create Complex Type.

Maintenant, commentez la fausse sélection et annulez le commentaire du corps de la procédure de magasin réel.

0
Max Favilli

Pendant l'importation

SET FMTONLY ON Peut être utilisé pour prendre le schéma sp.

Si vous modifiez le sp et souhaitez mettre à jour le nouveau, vous devez supprimer l'ancienne fonction définie du fichier edmx (du xml), car même si vous supprimez le sp du navigateur de modèle, il n'est pas supprimé dans edmx. Par exemple;

    <FunctionImport Name="GetInvoiceByNumber"     ReturnType="Collection(Model.Invoice_Result)">
       <Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
    </FunctionImport>

J'ai eu le même problème, et lorsque je supprime totalement la balise FuctionImport du sp correspondant, le modèle a été mis à jour à droite. Vous pouvez trouver la balise en recherchant le nom de la fonction dans Visual Studio.

0
Betul Bas

Il suffit de changer pour 

ALTER PROCEDURE [dbo].[usp_GetTestRecords] 
    --@p1 int = 0, 
    --@p2 int = 0
    @groupId nvarchar(10) = 0
AS
BEGIN
    SET NOCOUNT ON;


    SELECT * FROM CUSTOMERS WHERE Id =  @groupId

END
0
Nezam

Je sais que c'est un vieux fil, mais si quelqu'un a les mêmes problèmes, je raconterai mes ennuis.

Pour vous aider à trouver le problème, exécutez SQL Profileer lorsque vous ajoutez votre proc stocké. Ensuite, vous pouvez voir quelle structure d'entité transmet en tant que paramètres afin de générer votre jeu de résultats. J'imagine presque toujours qu'il transmettra des valeurs de paramètre nulles. Si vous générez SQL à la volée en concaténant des valeurs de chaîne et des valeurs de paramètre et que certaines sont nulles, SQL s'interrompra et vous n'obtiendrez pas de jeu de retour. 

Je n'ai pas eu besoin de générer des tables temporaires ou quoi que ce soit simplement une commande exec.

J'espère que ça aide

0
Rob White

Vous aurez peut-être de la chance en ouvrant le navigateur de modèle, puis en allant à Importations de fonctions, en double-cliquant sur la procédure stockée en question, puis en cliquant manuellement sur "Obtenir les informations sur la colonne", puis sur "Créer un nouveau type de complexe". Cela règle généralement le problème.

0
Captain Kenpachi