从 C# 调用时,来自 SQL Server 的标量值函数不返回值
Posted
技术标签:
【中文标题】从 C# 调用时,来自 SQL Server 的标量值函数不返回值【英文标题】:Scalar valued function from SQL Server not returning value when called from C# 【发布时间】:2022-01-06 04:51:22 【问题描述】:我被赋予了这个功能
CREATE FUNCTION [dbo].[GET_WEBGIS_ISSUE_NUM]
()
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @v_new_num int, @v_new_issue_num varchar(50);
SET @v_new_num = (SELECT COUNT(*) + 1
FROM [dbo].[WEBGIS_ISSUE]
WHERE [ISSUE_NUM] LIKE CONCAT(FORMAT(GETDATE(), 'yyMM'), '%'));
IF @v_new_num < 10
SET @v_new_issue_num = CONCAT(FORMAT(GETDATE(), 'yyMM'), '00', @v_new_num);
ELSE IF @v_new_num < 100
SET @v_new_issue_num = CONCAT(FORMAT(GETDATE(), 'yyMM'), '00', @v_new_num);
ELSE
SET @v_new_issue_num = CONCAT(FORMAT(GETDATE(), 'yyMM'), @v_new_num);
RETURN @v_new_issue_num
END;
我尝试从以下 C# 代码调用它
SqlConnection cnn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "[NEPS].[dbo].[GET_WEBGIS_ISSUE_NUM]";
//add any parameters the stored procedure might require
if (cmd.Connection.State == ConnectionState.Closed) //cmd.Connection.Open();
cnn.Open();
var o = cmd.ExecuteScalar();
//blabla
cnn.Close();
但是当我调试代码时,我一直收到 null。
注意:连接正常,已连接,当我尝试更改函数名称时会产生错误,当我通过 SQL Server 检查时,它也会返回适当的返回值。
【问题讨论】:
cmd.CommandType = CommandType.StoredProcedure
? GET_WEBGIS_ISSUE_NUM
不是存储过程;这是一个标量函数...
您需要使用 CommandType.Text
并运行 SELECT * FROM dbo.GET_WEBGIS_ISSUE_NUM()
- 但这真的 BADLY 闻起来像黑客在做与 IDENTITY
列相同的事情 - 只是不是正确和安全........
如果SEQUENCE
尝试服务于类似于IDENTITY
的目的但用于多个表,那么它可能是一个更好的选择。 SEQUENCE
在所有受支持(包括扩展支持)版本的 SQL Server 上都可用,所以如果你是这样的话,我看不出你为什么不使用它。
你需要一个实际的查询SELECT dbo.GET_WEBGIS_ISSUE_NUM()
。虽然为什么这个功能甚至存在是另一个问题。至少它应该是一个 inline 表值函数,它要快得多。您还需要使用using
处理您的连接和命令对象。而且你不需要if (cmd.Connection.State
,如果你刚刚创建它,它就没有理由打开
最后但并非最不重要的一点是,尝试使用 COUNT(*) + 1
生成“唯一”数字是高度可疑的,因为如果删除记录(即使没有并发),这似乎很容易破坏,所以你' d 必须提交一个绝对只追加的表。基于MAX
的方法似乎更稳定(但在并发下仍然不安全,请注意)。
【参考方案1】:
使用SELECT
语句返回标量函数结果更为常见。使用EXECUTE
时(由于CommandType.StoredProcedure
),还需要指定返回参数,执行后从参数中取出结果:
var result = cmd.Parameters.Add("@result", SqlDbType.VarChar, 50);
result.Direction = ParameterDirection.ReturnValue;
cmd.ExecuteNonQuery(); //ExecuteScalar will work too but the result is null and you still need to use the parameter
var o = result.Value;
正如您问题的 cmets 中所述,请考虑这种方法的并发影响。将返回重复值,直到行数发生变化。
【讨论】:
【参考方案2】:您将标量函数视为存储过程,而这种执行类型是错误的类型。您需要使用标量函数来“CommandType.Text”。
关于C#
部分的其他说明:
using
块与 SqlConnection
和 SqlCommand
一起使用(让 using 子句为您处理处置和关闭连接部分)。
查询应声明为const string
始终以分号结束查询(即使它在没有分号的情况下在 SQL Server 中运行)。
避免使用短名称,为变量选择易读的命名。
这是 C# 代码:
const string query = "SELECT [NEPS].[dbo].[GET_WEBGIS_ISSUE_NUM]();";
using(SqlConnection connection = new SqlConnection(connectionString))
using(SqlCommand command = new SqlCommand(query, connection))
connection.Open();
var result = command.ExecuteScalar();
// do stuff
对于函数GET_WEBGIS_ISSUE_NUM
,也许你可以用这一行避免额外的IF
s:
CREATE FUNCTION [dbo].[GET_WEBGIS_ISSUE_NUM]
()
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @v_new_num int, @v_new_issue_num varchar(50);
SET @v_new_num = (SELECT COUNT(*) + 1
FROM [dbo].[WEBGIS_ISSUE]
WHERE [ISSUE_NUM] LIKE CONCAT(FORMAT(GETDATE(), 'yyMM'), '%'));
SET @v_new_issue_num, FORMAT(GETDATE(), 'yyMM') + RIGHT('000' + CAST(@v_new_num AS VARCHAR), 4);
RETURN @v_new_issue_num
END;
【讨论】:
以上是关于从 C# 调用时,来自 SQL Server 的标量值函数不返回值的主要内容,如果未能解决你的问题,请参考以下文章
SqlDataReader 和 T-SQL:在使用来自 C# 的“异步”调用时使用“try...catch”和“throw”时不会传播异常
使用 C# 和存储过程从 SQL Server 检索 VarChar(MAX)