C# 存储过程有时返回short,有时返回int

C# 存储过程有时返回short,有时返回int,c#,.net,entity-framework-6,sql-server-2012,C#,.net,Entity Framework 6,Sql Server 2012,我使用的是一个遗留代码库,需要调用一个存储过程,不允许修改该存储过程。此存储过程返回一行或多行验证数据 结果集示例(两列,代码和文本): 或 在程序本身中,消息的选择仅为: Select 0 as code, 'success' as text 问题: 我正在使用实体框架将此存储过程的结果映射到自定义类: public class ValidationResult { public int code { get; set; } public string text { get;

我使用的是一个遗留代码库,需要调用一个存储过程,不允许修改该存储过程。此存储过程返回一行或多行验证数据

结果集示例(两列,代码和文本):

在程序本身中,消息的选择仅为:

Select 0 as code, 'success' as text
问题:

我正在使用实体框架将此存储过程的结果映射到自定义类:

public class ValidationResult
{
    public int code { get; set; }
    public string text { get; set; }
}
电话本身:

var result = context.Database.SqlQuery<ValidationResult>(@"old_sproc").ToList();
var result=context.Database.SqlQuery(@“old_存储过程”).ToList();
我已经编写了一些集成测试,并注意到当过程返回成功消息时,0显示为一个
。当它返回非零消息时,会显示为
int
。我假设将
code
设置为int,那么short就适合了。不幸的是,我的成功测试出现以下异常:

从物化的“System.Int16”类型到“System.Int32”类型的指定强制转换无效

当我将
code
切换到短路以使成功测试通过时,失败测试失败,出现以下异常:

从物化的“System.Int32”类型到“System.Int16”类型的指定强制转换无效

ADO.NET是一个答案


一种解决方案是回退到ADO.NET的
SqlDataReader
对象,因此我将其作为回退解决方案。我想知道我是否可以在EF端做些什么来让它工作。

将静态消息代码转换为int:

Select cast(0 as int) as code, 'success' as text
这确保返回的文本与另一个查询返回的int一致。保留
ValidationResult.code
声明为
int


注意:我知道我遗漏了问题中关于SP不能修改的部分,但鉴于这使得答案相当复杂,我将把这个留给可能有相同问题的其他人,但是,通过修改SP可以更轻松地解决此问题。如果SP中存在返回类型不一致的情况,则可以使用此选项。

如果无法更改存储过程本身,则可以创建一个以某种方式更改数据的包装器存储过程,并让EF调用它


当然不理想,但可能是一种选择。

如果没有更好的解决方案,您可以使用一种变通方法。设为int。它适用于所有错误代码。如果您得到一个异常,您知道结果是成功的,因此您可以为该特定异常添加try/catch。这并不漂亮,取决于它运行的程度,它可能会影响性能

另一个想法是,您是否尝试过将
code
的类型更改为
object

注意:如果您使用的是SQL Server 2012或更高版本,请参见,它显示了一种更简洁、更简洁的方法来完成此处描述的相同操作。)

这是一个留在EF land中的解决方案,不需要任何数据库模式更改

由于您可以将任何有效的SQL传递给
SqlQuery
方法,因此没有什么可以阻止您传递多语句脚本:

  • 声明一个临时表
    
  • 执行
    s存储过程并将其结果插入到临时表中
  • 选择该临时表的最终结果
  • 最后一步是可以应用任何进一步的后处理,例如类型转换

    const string sql = @"DECLARE @temp TABLE ([code] INT, [text] VARCHAR(MAX));
                         INSERT INTO @temp EXECUTE [old_sproc];
                         SELECT CONVERT(INT, [code]) AS [code], [text] FROM @temp;";
                         //     ^^^^^^^^^^^^^      ^^^^^^^^^^^
                         // this conversion might not actually be necessary
                         // since @temp.code is already declared INT, i.e.
                         // SQL Server might already have coerced SMALLINT
                         // values to INT values during the INSERT.
    
    var result = context.Database.SqlQuery<ValidationResult>(sql).ToList();
    
    conststringsql=@“声明@temp表([code]INT,[text]VARCHAR(MAX));
    插入@temp EXECUTE[旧存储过程];
    从@temp;中选择CONVERT(INT,[code])作为[code],[text];
    //     ^^^^^^^^^^^^^      ^^^^^^^^^^^
    //这种转换实际上可能并不必要
    //因为@temp.code已经声明为INT,即。
    //SQL Server可能已经强制了SMALLINT
    //插入期间,将值转换为INT值。
    var result=context.Database.SqlQuery(sql.ToList();
    
    (这是对的后续操作。它仅适用于和更高版本。)

    简短答复: 查询文本
    “old\u存储过程”
    实际上是
    “EXECUTE old\u存储过程”
    的缩写T-SQL。我之所以提到这一点,是因为很容易认为
    SqlQuery
    以某种方式专门处理存储过程的名称;但是不,这实际上是一个常规的T-SQL语句

    在这个答案中,我们将只修改您当前的SQL一点点

    使用带有结果集的
    子句进行隐式类型转换:
    让我们继续讨论您已经在做的事情:
    通过
    SqlQuery
    执行存储过程。从SQL Server 2012开始,支持一个名为
    的可选子句,该子句带有结果集,允许您指定希望返回的结果集。如果实际结果集与该规范不匹配,SQL Server将尝试执行隐式类型转换

    在您的情况下,您可以这样做:

    var sql = "EXECUTE old_sproc WITH RESULT SETS ((code INT, text VARCHAR(MAX)))";
    var result = context.Database.SqlQuery<ValidationResult(sql).ToList();
    
    var sql=“使用结果集((code INT,text VARCHAR(MAX))执行旧存储过程”;
    
    var result=context.Database.SqlQuery忽略int/short。同一个数字的文本总是相同的,对吗?只获取文本。我有一个开关箱。是的,这是一种黑客行为,但除非你能解决问题的根源(你说你是不允许的),否则你应该使用这种黑客行为,它将花费最少的时间来创建,并且不会给下一个维护代码的人带来问题。如果此存储过程是遗留的,那么将来将不会有任何新类型的结果。这个解决方案加上一个漂亮的注释解决了这个问题,让您可以回到其他地方创造价值。

    在实体框架数据建模器页面(
    模型浏览器
    )中
    const string sql = @"DECLARE @temp TABLE ([code] INT, [text] VARCHAR(MAX));
                         INSERT INTO @temp EXECUTE [old_sproc];
                         SELECT CONVERT(INT, [code]) AS [code], [text] FROM @temp;";
                         //     ^^^^^^^^^^^^^      ^^^^^^^^^^^
                         // this conversion might not actually be necessary
                         // since @temp.code is already declared INT, i.e.
                         // SQL Server might already have coerced SMALLINT
                         // values to INT values during the INSERT.
    
    var result = context.Database.SqlQuery<ValidationResult>(sql).ToList();
    
    var sql = "EXECUTE old_sproc WITH RESULT SETS ((code INT, text VARCHAR(MAX)))";
    var result = context.Database.SqlQuery<ValidationResult(sql).ToList();
    
    var result = context.Database.SqlQuery<ValidationResult>(@"old_sproc").ToList();
    
    var sql = "EXECUTE old_sproc WITH RESULT SETS ((code INT, text VARCHAR(MAX)))";
    var result = context.Database.SqlQuery<ValidationResult(sql).ToList();
    
    select  1 AS Code , 'Text' as text
    
    RETURN @@ROWCOUNT