.NET 9 Blazor项目中动态拼接带SqlParameter的存储过程EXEC语句时遇类型转换错误的求助
各位好,我现在在开发一个基于.NET 9的Blazor Web应用,遇到了一个关于动态拼接带SqlParameter的存储过程执行语句的问题,想请大家帮忙看看。
我的需求是:写一个方法,能动态处理SqlParameter[]参数,把它们和EXEC {storedProcedure}拼接成FormattableString,不用手动逐个指定参数的索引(比如不能每次都写{sqlParameterparam[0]}, {sqlParameterparam[1]}这种固定索引的写法)。
我一开始写的方法是这样的:
public static FormattableString FromSqlSQLParamDynamic(string storedProcedure, string[] paramName, params object?[] parameters) { if (paramName.Length != parameters.Length) { throw new ArgumentException("Parameters count mismatch."); } SqlParameter[] sqlParameterparam = [.. paramName.Select((name, i) => new SqlParameter(name, parameters[i] ?? DBNull.Value) )]; FormattableString sqlQuery = $"EXEC {storedProcedure} {string.Join(", ", sqlParameterparam.Select(p => p))}"; // 预期生成:EXEC myStoredProcedure id, random_text, key return sqlQuery; }
我确认sqlParameterparam的初始化是正确的,但动态拼接的时候却报错了,错误信息是:
An unhandled exception occurred while processing the request. SqlException: Error converting data type nvarchar to int. Microsoft.Data.SqlClient.SqlCommand+<>c.b__211_0(Task result)
我理解这个错误是因为参数的数据类型没有正确对应,但如果我把拼接语句改成手动指定每个参数的索引,就完全正常:
FormattableString sqlQuery = $"EXEC {storedProcedure} {sqlParameterparam[0]}, {sqlParameterparam[1]}, {sqlParameterparam[2]}";
但这种写法不够动态,参数数量变了就得改代码,不是我想要的效果。
下面是我的相关代码上下文:
SQLServerHelper类的GetRandTextsFromSqlROAsync方法
using Microsoft.EntityFrameworkCore; using MyProject.Models.SQLServer; using MyProject.Data.SQLServer; class SQLServerHelper(SQLServerContext context) { private readonly SQLServerContext _context = context; public async Task<List<RandText>> GetRandTextsFromSqlROAsync(string storedProcedure, params object?[] parameters) { string[] paramNames = ["id", "random_text", "key"]; if (parameters.Length != paramNames.Length) { throw new ArgumentException("Parameters count mismatch."); } var sqlParam = SQLDbSettings.FromSqlSQLParamDynamic(storedProcedure, paramNames, parameters); return await _context.RandText .FromSql(sqlParam) .AsNoTracking() .ToListAsync(); } }
Blazor页面中的调用代码
@page "/sql_server/crud/randtext" @rendermode InteractiveServer @using Microsoft.EntityFrameworkCore @using MyProject.Models.SQLServer @using MyProject.Data.SQLServer @implements IAsyncDisposable @inject IDbContextFactory<MyProject.Data.SQLServer.BlazorSQLServerContext> DbFactory <!-- 客户端脚本部分 --> @code { private SQLServerContext context = default!; private List<RandText> randText = new(); protected override async Task OnInitializedAsync() { context = DbFactory.CreateDbContext(); var sQLServerHelper = new SQLServerHelper(context); randText = await sQLServerHelper.GetRandTextsFromSqlROAsync("myStoredProcedure", null, null, "SEE_RECORDS"); } public async ValueTask DisposeAsync() { await context.DisposeAsync(); } }
存储过程定义
CREATE PROCEDURE myStoredProcedure @id INT, @random_text VARCHAR(50), @key VARCHAR(100) AS BEGIN SET NOCOUNT ON; DECLARE @successful VARCHAR(20) = 'SUCCESSFUL'; IF @key = 'SEE_RECORDS' BEGIN SELECT RandText.id ,RandText.random_text ,RandText.dt_stamp FROM myDB.dbo.myTable AS RandText WHERE RandText.disable = 0 ORDER BY RandText.dt_stamp DESC END ELSE BEGIN SELECT CONCAT('@key parameter value: ', '''', @key, '''', ' cannot be found.') SP_OUTPUT END END
我之前参考过官方的示例,比如这种写法是可行的:
var user = new SqlParameter("user", "johndoe");
var blogs = await context.Blogs
.FromSql($"EXECUTE dbo.GetMostPopularBlogsForUser {user}")
.ToListAsync();
官方还提示:参数必须完全匹配存储过程定义,要注意参数顺序、类型对应,也可以考虑使用命名参数。
但我就是不知道为什么动态拼接的时候会出类型转换错误,手动指定索引就正常,有没有办法实现动态拼接的同时保证参数类型正确传递呢?麻烦各位指点一下,谢谢!
内容来源于stack exchange

