J'utilise Entity Framework et ai mis à jour une table et sa procédure stockée, mais l'erreur suivante s'affiche lorsque la procédure stockée est appelée.
Le lecteur de données est incompatible avec le .__ spécifié. 'FormValueModel.Valuation'. Un membre du type 'ValuationId' fait ne pas avoir de colonne correspondante dans le lecteur de données avec le même nom.
ValuationId est ma clé principale que je veux incrémenter automatiquement.
Je peux exécuter la procédure stockée trouvée à partir du studio de gestion SQL. Lorsque je lance mon application, elle écrit dans la base de données, mais le message d'erreur apparaît.
Je ne connais pas Entity Framework et je ne connais que les bases, et je pense que cela pourrait être un problème de mappage de model.edmx.
Quelle serait la procédure correcte pour recréer et mapper les tables et les procédures stockées dans le modèle?
Procédure stockée.
ALTER PROCEDURE [dbo].[ValuationCreate]
@TrackingNumber varchar(100),
@FormMobiValuationId varchar(100),
@ValuationPropertyId int,
@ValuationFileName varchar(50)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
DECLARE @ErrorMessage varchar(1000)
BEGIN TRANSACTION
--Insert to Valuation
INSERT INTO [Valuation]
(
TrackingNumber,
FormMobiValuationId,
ValuationPropertyId, -- new
ValuationFileName,
Date,
ValuationStatus,
IsActive
)
VALUES
(
@TrackingNumber,
@FormMobiValuationId,
@ValuationPropertyId,--new
@ValuationFileName,
GETDATE(),
1, --Created
1
)
IF @@ERROR > 0
BEGIN
SET @ErrorMessage = 'Valuation Insert failed'
GOTO ErrorHandler
END
ELSE
BEGIN
COMMIT TRANSACTION
RETURN
END
ErrorHandler:
RAISERROR(@ErrorMessage,16,1);
ROLLBACK TRANSACTION
RETURN -1
Appel C # où une erreur survient, le message d'erreur apparaît sur la dernière ligne.
public ObjectResult<Valuation> ValuationCreate(global::System.String trackingNumber, global::System.String formMobiValuationId, Nullable<global::System.Int32> valuationPropertyId, global::System.String valuationFileName)
{
ObjectParameter trackingNumberParameter;
if (trackingNumber != null)
{
trackingNumberParameter = new ObjectParameter("TrackingNumber", trackingNumber);
}
else
{
trackingNumberParameter = new ObjectParameter("TrackingNumber", typeof(global::System.String));
}
ObjectParameter formMobiValuationIdParameter;
if (formMobiValuationId != null)
{
formMobiValuationIdParameter = new ObjectParameter("FormMobiValuationId", formMobiValuationId);
}
else
{
formMobiValuationIdParameter = new ObjectParameter("FormMobiValuationId", typeof(global::System.String));
}
ObjectParameter valuationPropertyIdParameter;
if (valuationPropertyId.HasValue)
{
valuationPropertyIdParameter = new ObjectParameter("ValuationPropertyId", valuationPropertyId);
}
else
{
valuationPropertyIdParameter = new ObjectParameter("ValuationPropertyId", typeof(global::System.Int32));
}
ObjectParameter valuationFileNameParameter;
if (valuationFileName != null)
{
valuationFileNameParameter = new ObjectParameter("ValuationFileName", valuationFileName);
}
else
{
valuationFileNameParameter = new ObjectParameter("ValuationFileName", typeof(global::System.String));
}
return base.ExecuteFunction<Valuation>("ValuationCreate", trackingNumberParameter, formMobiValuationIdParameter, valuationPropertyIdParameter, valuationFileNameParameter);
}
Le message signifie que les résultats de la procédure stockée ne contiennent pas de colonne nommée ValudationId
. Vérifiez votre déclaration select
et exécutez-la dans SSMS pour vous assurer de ramener cette colonne.
EDIT: Votre procédure ne contient pas d'instruction select
. Vous devez sélectionner la valeur d'identité insérée (à l'aide de la fonction scope_identity()
, par exemple) afin que EF puisse la mapper à l'entité.
Par exemple,
insert into Table
(
Col1,
Col2
)
values
(
1,
2
)
select scope_identity() as IdentityColName
De plus, en passant, vous n'avez pas besoin de toutes ces transactions dans votre déclaration d'insertion; vous n'avez qu'une seule instruction (votre insertion) qui modifie des données.
Pour ceux qui obtiennent toujours la même erreur, assurez-vous que vous pointez/êtes connecté à la bonne base de données. Après avoir passé des heures, j'ai découvert que je travaillais sur la base de données active et non sur la base de test. Et bien sûr, les modifications que j'ai apportées à la procédure stockée dans la base de données de test n'avaient pas d'équivalence dans la base de données active.
Dans mon cas, il renvoyait des données, mais le nom de la colonne, non fourni, en raison d'une instruction CAST
qui n'avait pas d'alias de nom de colonne, l'a fait devenir blank. L'erreur de nom de colonne manquante a fini par générer l'échec de mappage signalé par EF.
En effectuant un appel dans SSMS et en affichant le résultat, ce résultat a révélé cette erreur évidente maintenant:
Nommer la colonne en SQL à laquelle EF s’attendait a résolu le problème.
Avinash, bon appel sur ExecuteSQLCommand et 'non-query'. Cependant, Pornster fait référence à ExecuteFunction pour appeler un SP et renvoyer les résultats . Pour résoudre ce problème, supprimez la déclaration "return" de votre SP et cela fonctionnera . Lorsque vous utilisez une instruction return, le SP renverra un int et non votre requête sélectionnée.
Étrange, j'ai résolu ce problème en ajoutant la commande GO à la fin de la procédure stockée.
J'ai corrigé ma version de ce problème en parcourant le modèle dans VS, en trouvant le processus stocké sous Importations de fonction et en modifiant le type de retour de la fonction sur Aucun
Si vous insérez/supprimez/mettez à jour (ceux-ci sont considérés par EF comme "non interrogés") et peuvent être appelés par notre code à l'aide de
context.Database.ExecuteSqlCommand(insert into Table (Col1,Col2) values (1,2));
Mais si vous faites une requête de sélection pour une instruction SQL brute, utilisez
context.DbSet<Table_name>.SqlQuery(select * from table_name).ToList();
ou context.Database.SqlQuery (select * from nom_table) .ToList ();
La fonction SqlQuery (), dans EF, pour des raisons étranges, lève l'exception avec l'opération Insert/delete/update. (L'exception levée est "Un membre du type n'a pas de colonne correspondante dans le lecteur de données avec le même nom.") Mais l'opération a effectivement été effectuée si vous ouvrez votre SQL Management Studio et vérifiez les entrées.
L'un des scénarios importants est de renvoyer des résultats différents selon les conditions. Par exemple, lorsque vous utilisez la commande IF, vous pouvez éventuellement renvoyer un jeu de résultats différent pour chaque branche. Lorsque EF définit la collection renvoyée lors de l'importation du SP, attendez-vous à une collection [type un] mais récupérez une collection [type 2] puis générez une erreur si aucune colonne ne correspond au lecteur de données. Ainsi, tout SP avec un jeu de résultats renvoyé doit renvoyer la même collection (en tant que nom de colonne et nombre) de toutes ses parties, comme ci-dessous:
IF (Condition 1)
BEGIN
SELECT [column1], [column2], [column3], ... FROM [...]
END
ELSE
BEGIN
SELECT [column1], [column2], [column3], ... FROM [...]
END
Cela a fonctionné pour moi dans le même numéro et espérons être utile.
Mettre à jour:
Une autre fois, je recevais ce message d'erreur lorsque j'avais le mauvais type de collection de retour dans Function Imports
. Vraiment, je ne m'attendais pas à des valeurs de type ou de collection et j'ai peut-être simplement besoin d'un entier comme paramètre de sortie, mais j'ai oublié de définir Returns a Collection Of
à None
. Donc, si vous ne vous attendez pas à un résultat, allez dans votre modèle et dans Model Browser
> Function Imports
, cliquez avec le bouton droit de la souris sur SP, cliquez sur Modifier, puis cochez la section Returns a Collection Of
et réglez-la sur None
.
Dans mon cas, comme suggéré par Brett Jones, la solution consistait à supprimer l'instruction "RETURN" de ma procédure stockée , en ne laissant simplement que la partie "SELECT". Je passe des heures et des heures à essayer toutes sortes de solutions que j'ai trouvées en ligne, mais c'était aussi simple que cela.
Surveillez également les cas où le fichier EDMX de votre modèle remappe le nom de colonne de la base de données en un nom différent de l'objet de classe correspondant.
Dans mon cas, la procédure stockée renvoyait des colonnes avec des espaces dans les noms [Monthly Bill Rate]
. Afin de gérer cela, le nom de la colonne a été remappé dans le modèle.
À un moment donné, la colonne de retour SP a été renommée en [MonthlyBillRate]
, mais le modèle n'a pas été mis à jour pour correspondre. Lorsque j'ai cherché le code pour MonthlyBillRate
, il était dans le fichier Designer.cs
sous la forme MonthlyBillRate
. Je ne pouvais pas comprendre pourquoi je recevais l'exception. En regardant de plus près, j'ai trouvé le nom remappé dans l'EDMX.
Le remappage a lieu dans la balise ScalarProperty
via l'attribut ColumnName
.
<FunctionImportMapping FunctionImportName="BillingReport" FunctionName="Model.Store.BillingReport">
<ResultMapping>
<ComplexTypeMapping TypeName="Model.BillData">
<ScalarProperty Name="MonthlyBillRate" ColumnName="Monthly Bill Rate" />
</ComplexTypeMapping>
</ResultMapping>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public Nullable<global::System.Decimal> MonthlyBillRate
{
... etc ...
}
Cela ne s'applique pas à ce cas particulier, mais j'ai eu un problème similaire dans lequel la même erreur a été générée, mais le type de membre spécifié était une colonne de mon choix post-fixée avec 1 ... colonne deux fois dans ma sélection .... exemple: -
SELECT
CustomerId,
FirstName,
LastName,
CustomerId
FROM
Customers
Vous utilisez peut-être select sans donner le nom d’alias pour column.