我添加了 sql 参数,但出现错误“未添加”

I add sql parameters but I get the error "not added"

提问人:Musa AKYUZ 提问时间:10/27/2023 最后编辑:CharliefaceMusa AKYUZ 更新时间:10/27/2023 访问量:59

问:

我收到此错误:

过程或函数“GetCurrencyAtDate”需要参数“@soruceCurrencyId”,但未提供该参数。

这是我的存储过程:

ALTER PROCEDURE [dbo].[GetCurrencyAtDate]
    @soruceCurrencyId int,
    @targetCurrencyId int,
    @year int,
    @month int,
    @day int,
    @result decimal OUTPUT
AS
BEGIN
    DECLARE @targetDate DATETIME
    SET @targetDate = DATEADD(DAY, @day - 1, DATEADD(MONTH, @month - 1, DATEADD(YEAR, @year - 1900, 0)))

    SELECT TOP 1 @result = Currency
    FROM KurTable
    WHERE SourceKur = @soruceCurrencyId
      AND TargetKur = @targetCurrencyId
      AND Date <= @targetDate
    ORDER BY ABS(DATEDIFF(DAY, Date, @targetDate));
END

我正在使用 Entity Framework Core 并通过函数编写命令:DbContext

using (var context = new EfDataContext())
{
    var date = DateTime.Now;
    var year = date.Year;
    var month = date.Month;
    var day = date.Day;

    var resultParameter = new SqlParameter
                {
                    ParameterName = "@result",
                    Direction = ParameterDirection.Output,
                    SqlDbType = SqlDbType.Decimal,
                };

    var command = context.Database.GetDbConnection().CreateCommand();
    command.CommandType = CommandType.StoredProcedure;
    command.CommandText = "GetCurrencyAtDate";

    command.Parameters.Add(new SqlParameter("@sourceCurrencyId", SqlDbType.Int) { Value = sourceCurrency });
    command.Parameters.Add(new SqlParameter("@targetCurrencyId", SqlDbType.Int) { Value = targetCurrency });
    command.Parameters.Add(new SqlParameter("@year", SqlDbType.Int) { Value = year });
    command.Parameters.Add(new SqlParameter("@month", SqlDbType.Int) { Value = month });
    command.Parameters.Add(new SqlParameter("@day", SqlDbType.Int) { Value = day });
    command.Parameters.Add(resultParameter);

    context.Database.OpenConnection();

    var result = await command.ExecuteScalarAsync();
    currencyValue = Convert.ToDecimal(result);
}

问题出在哪里?

C# sql-server 存储过程 ado.net sqlclient

评论

1赞 Yong Shun 10/27/2023
在存储过程中,第一个参数 name 而不是 .@soruceCurrencyId@sourceCurrencyId
0赞 Musa AKYUZ 10/27/2023
@YongShun ı 尝试了多种方法来获取结果值,但每次都返回 null。这一次再次返回 null。这是另一个问题
2赞 Panagiotis Kanavos 10/27/2023
首先,这不是 EF 代码,而是普通的旧 ADO.NET。您(错误地)使用 DbContext 只是为了获取 SqlConnection。您绝对不需要 EF 来做到这一点。其次,您拼写错误为 .最后,您的代码从不检查 的值。它尝试检查不存在的返回值sourceCurrencyIdsoruceCurrencyId resultParameter
0赞 Panagiotis Kanavos 10/27/2023
@YongShun代码根本不会尝试读取输出参数。它尝试读取任何查询结果,但存储过程不返回任何查询结果
0赞 Yong Shun 10/27/2023
您应该能够通过以下方式获得价值currencyValue = resultParameter.Value;

答:

2赞 Panagiotis Kanavos 10/27/2023 #1

此代码存在几个问题,即使它有效,也会使其非常慢 - 它滥用 EF Core 仅执行 ADO.NET 代码,忘记关闭连接,存储过程本身过于复杂,例如用于执行也会执行的操作。日期作为部分传递,然后以复杂的方式重建,而不是使用ABS(DATEDIFF(DAY, Date, @targetDate))ORDER BY DateDATEFROMPARTS

以下所有选项都可以与存储过程一起使用,但在这种情况下,它只是不需要。

要从表格中实际获取最新汇率,您只需要使用 Dapper 来减少样板:

var sql=@" SELECT TOP 1 Currency
    FROM KurTable
    WHERE SourceKur = @fromCur
        AND TargetKur = @toCur
        AND Date <= @date
    ORDER BY Date DESC";

using (var con=new SqlConnection(connectionString))
{
    var rate=con.ExecuteScalar<decimal>(sql,new { 
        fromCur=sourceCurrency,
        toCur=targetCurrency,
        date=DateTime.Today
    });
}

就这样。Dapper 将创建一个 SqlCommand,其参数基于匿名类型属性,使用相同的名称和类型,打开连接,执行查询,返回结果关闭连接。它还将对命令和映射进行处理,以便下次不必重新生成它。

在此示例中,连接是在块中创建的,因此一旦块退出,它就会被释放。using

如果需要,也可以直接使用 DbContext 的连接。

var con=context.Database.GetDbConnection();
var rate=con.ExecuteScalar<decimal>(sql,new { 
        fromCur=sourceCurrency,
        toCur=targetCurrency,
        date=DateTime.Today
});

EF 核心 7

EF Core 7 也使用 SqlQuery 提供相同的功能:

var rate=context.Database
    .SqlQuery<decimal>($@"SELECT Currency
        FROM KurTable
        WHERE SourceKur = {sourceCurrency}
            AND TargetKur = {targetCurrency}
            AND Date <= {targetDate}
        ORDER BY Date DESC")
    .FirstOrDefault();

这不是字符串插值操作。formattable 字符串用于生成参数并填充其值。

SqlQuery 返回一个 IQueryable<>这意味着我们也可以使用它来指定排序顺序:OrderByDescending

var rate=context.Database
    .SqlQuery<decimal>($@"SELECT Currency,Date
        FROM KurTable
        WHERE SourceKur = {sourceCurrency}
            AND TargetKur = {targetCurrency}
            AND Date <= {targetDate}")
    .OrderByDescending(c=>c.Date)
    .Select(c=>c.Currency)
    .FirstOrDefault();

当然,如果表是一个实体,我们可以只编写一个 LINQ 查询。

var rate=context.ExchangeRates
                .Where(xr=>xr.SourceCurrency==sourceCurrency &&
                           xr.TargetCurrency==targetCurrency &&
                           xr.Date <= targetDate)
                .OrderByDescending(xr=>xr.Date)
                .Select(xr=>xr.Currency)
                .FirstOrDefault();

使用存储过程

返回特定日期的最新汇率的存储过程如下所示:

ALTER PROCEDURE [dbo].[GetCurrencyAtDate]
    @sourceCurrencyId INT,
    @targetCurrencyId int,
    @targetDate DATE
AS
BEGIN
    SELECT TOP 1 Currency
    FROM KurTable
    WHERE SourceKur = @soruceCurrencyId
        AND TargetKur = @targetCurrencyId
        AND Date <= @targetDate
    ORDER BY Date DESC
END

这可以使用 Dapper 或 EF Core 7 调用:

var rate= con.ExecuteScalar<decimal>("GetCurrencyAtDate",
    new { 
        sourceCurrencyId =sourceCurrency,
        targetCurrencyId =targetCurrency,
        targetDate =DateTime.Today
    },
    commandType: CommandType.StoredProcedure);

在 EF Core 7 中:

var rate=context.Database
    .SqlQuery<decimal>($"exec GetCurrencyAtDate 
    @sourceCurrencyID={sourceCurrency}, 
    @targetCurrencyId= {targetCurrency}, 
    @targetDate = {targetDate}")
    .FirstOrDefault();

评论

0赞 Musa AKYUZ 10/27/2023
谢谢你的所有解释。让我了解更多。
0赞 siggemannen 10/27/2023
解释得非常好!
1赞 Ashish Ingle 10/27/2023 #2

只需使用 NULL 默认值声明存储过程参数即可。

ALTER PROCEDURE [dbo].[GetCurrencyAtDate]
@soruceCurrencyId int = NULL,
@targetCurrencyId int = NULL,
@year int = NULL ,
@month int = NULL,
@day int = NULL,
@result decimal OUTPUT