使用实体框架获取存储过程输出参数抛出映射错误:数据读取器与指定的不兼容

Posted

技术标签:

【中文标题】使用实体框架获取存储过程输出参数抛出映射错误:数据读取器与指定的不兼容【英文标题】:Getting stored procedure output parameter with Entity Framework is throwing a mapping error: The data reader is incompatible with the specified 【发布时间】:2017-12-31 21:14:28 【问题描述】:

我正在开发一个遗留应用程序,我正在尝试调用一个返回整数的存储过程,但我遇到了映射错误。错误本身就是这个:

数据读取器与指定的 IF_Insert_Incidents_Citizen' 不兼容。类型的成员“intID”在数据读取器中没有同名的对应列。

我尝试将名称更改为 _intId,因为我知道 Entity Framework 会将所有以 @variable 开头的名称转换为 _variable

奇怪的是,如果我使用 LinqPad 并调用存储过程,我会得到正确的值 @intID

这是存储过程:

CREATE PROCEDURE [dbo].[IF_Insert_Incidents_Citizen]
    @chvFolio varchar(50),
    @chvOwner_Name varchar(100),
    @chvOwner_Address varchar(100),
    @dtsDate_Created smalldatetime, 
    @intUser_ID integer, 
    @chvDescription varchar(250), 
    @chvEmail varchar(50), 
    @intIncident_Type_ID integer, 
    @intIncident_Status integer, 
    @intID int Output
AS
    SET nocount on

    DECLARE @intErrorCode INT
    DECLARE @intApp_ID INT

    SELECT @intIncident_Type_ID = CAST(Param_Value AS INT)
    FROM IF_Parameters
    WHERE Param_Name = 'Citizen_Incident_Type_ID'

    BEGIN
        --Get an application id
        INSERT INTO MasterApplication (DateCreated) 
        VALUES (getdate())

        SELECT @intApp_ID = SCOPE_IDENTITY()

        INSERT INTO IF_Incidents
        (
            Folio, 
            Owner_Name,
            Owner_Address,
            Date_Created, 
            User_ID, 
            Description, 
            Incident_Type_ID, 
            Incident_Status,
            App_ID
        )
        VALUES 
        (
            @chvFolio, 
            @chvOwner_Name,
            @chvOwner_Address,
            @dtsDate_Created, 
            @intUser_ID, 
            @chvDescription ,
            @intIncident_Type_ID, 
            @intIncident_Status,
            @intApp_ID 
        )
        Select @intErrorCode = @@Error, @intID = SCOPE_IDENTITY()

        --Insert Complaint
        INSERT INTO IF_Complaints
        (
            Incident_ID, 
            Complainant_ID, 
            Description ,  
            User_ID, 
            Officer_Assigned_ID, 
            Complaint_Status_ID, 
            Date_Made,
            Email_Complainant
        )
        VALUES 
        (
            @intID, 
            Null, 
            @chvDescription + ' at ' + @chvOwner_Address, 
            1, 
            1, 
            1, 
            @dtsDate_Created ,
            @chvEmail
        )
        Select @intErrorCode = @@Error--, @intID = SCOPE_IDENTITY()
    End

    Return @intErrorCode

在存储过程中可以看到,intId在插入后总是得到一个增量值,调用SCOPE_IDENTITY()

现在我尝试通过以下方式使用实体框架调用我的存储过程:

var bptRep = DependencyFactory.GetDependency<ICetRepository<IF_Insert_Incidents_Citizen>>();
var parameters = GetIfInsertIncidentsCitizenParamenters();
var res = bptRep.GetAllFromStoredProcedure(storedProcedure, parameters);

仓库使用这个类

//class with just the return value
public class IF_Insert_Incidents_Citizen

    public int? intID  get; set; 

这里我得到了存储过程的所有参数

 private IEnumerable<SqlParameter> GetIfInsertIncidentsCitizenParamenters()
 
        // Create Parameters
        var parameters = new List<SqlParameter>();

        var param1 = CreateSqlParameter("@chvFolio", SqlDbType.NVarChar, ParameterDirection.Input, criteria["chvFolio"].Value, 50); 

        parameters.Add(param1);

          ....

        var paramX = CreateSqlParameter("@intIncident_Status", SqlDbType.Int, ParameterDirection.Input, 10);
        parameters.Add(paramX);

        var outparam = CreateSqlParameter("@intID", SqlDbType.Int, ParameterDirection.InputOutput, DBNull.Value);
        parameters.Add(outparam);


public IQueryable<TEntity>GetAllFromStoredProcedure(string storedProcedure, IEnumerable<SqlParameter> parameters)

    var result = Context.Database.SqlQuery<TEntity>(storedProcedure, parameters).AsQueryable();    
    return result;

我遵循了@GertArnold 建议但仍然无法正常工作的方法,这是我的参数和代码

var returnCode = new SqlParameter("@ReturnCode", SqlDbType.Int);
returnCode.Direction = ParameterDirection.Output;

var sql = "exec IF_Insert_Incidents_Citizen @chvFolio, @chvOwner_Name, @chvOwner_Address, @dtsDate_Created, @intUser_ID, @chvDescription, @chvEmail, @intIncident_Type_ID, @intIncident_Status, @intID OUT";

var data = ctx.Database.SqlQuery<object>(sql, returnCode, parameters);

var result = data.FirstOrDefault();

【问题讨论】:

看this @GertArnold 我一直在从那里测试该方法,但我遗漏了一些东西,因为仍然失败。我在 storeprocedure 的调用中引入了 OUT 并尝试了一个工作,我把 ReturnCode 以及不工作,我得到另一个错误 【参考方案1】:

最后@GertArnold 让我找到了正确的方向,如果你还没有读过这篇文章,请去做,会为你节省很多时间Stored procedures with output parameters using SqlQuery in the DbContext API

我只是在 exec 调用中将 OUT 添加到我的@intId,无需创建 ReturnCode 变量并调用exec @ReturnCode = ....

var returnCode = new SqlParameter("@ReturnCode", SqlDbType.Int);
returnCode.Direction = ParameterDirection.Output;

我的代码最终看起来像这样,请注意在参数上调用.ToArray 方法,而不是将IEnumerableIList 的集合传递给方法

var sql = "exec IF_Insert_Incidents_Citizen @chvFolio, @chvOwner_Name, @chvOwner_Address, @dtsDate_Created, @intUser_ID, @chvDescription, @chvEmail, @intIncident_Type_ID, @intIncident_Status, @intID OUT";

var data = ctx.Database.SqlQuery<object>(sql, returnCode, parameters.ToArray());

var result = data.FirstOrDefault();

【讨论】:

以上是关于使用实体框架获取存储过程输出参数抛出映射错误:数据读取器与指定的不兼容的主要内容,如果未能解决你的问题,请参考以下文章

更新存储过程实体框架抛出“验证FunctionImport名称是唯一的”错误

实体框架中存储过程的返回值映射

无法在实体框架中映射 SYS_REFCURSOR

实体框架 - 您能否将导入的存储过程的结果类型映射到自定义实体类型?

在实体框架数据库优先方法中,如何从存储过程返回多个结果集?

领域模型和实体框架之间的存储库模式和映射