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