“过程或函数“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);
}
问题出在哪里?
此代码存在几个问题,即使它有效,也会使其非常慢 - 它仅滥用 EF Core 来执行 ADO.NET 代码,它忘记关闭连接,存储过程本身过于复杂,例如使用
ABS(DATEDIFF(DAY, Date, @targetDate))
做 ORDER BY Date
也会做的事情。日期作为部分传递,然后以复杂的方式重建,而不是使用 DATEFROMPARTS
下面的所有选项都可以与存储过程一起使用,但在本例中,不需要它。
要真正从表格中获取最新汇率,您需要使用例如 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 Core 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();
这不是字符串插值操作。格式化字符串用于生成参数并填充它们的值。
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();