如何在 ASP.NET Core MVC 中使用 ADO.NET 向存储过程添加参数?

Posted

技术标签:

【中文标题】如何在 ASP.NET Core MVC 中使用 ADO.NET 向存储过程添加参数?【英文标题】:How can I add parameters to a stored procedure using ADO.NET in ASP.NET Core MVC? 【发布时间】:2021-05-03 17:52:00 【问题描述】:

我想在 ASP.NET Core MVC 中执行具有 ADO.NET 输出参数的存储过程。但是我收到一条错误消息,说我的存储过程需要一个我已经设置的参数。

我的存储过程是这样的:

ALTER PROCEDURE [dbo].[spSearch] 
    @Notes nvarchar(max) = NULL, 
    @StartDate datetime = NULL, 
    @EndDate datetime = NULL,
    @UserId nvarchar(450),
    @Result money OUTPUT
AS 
BEGIN
    IF (@StartDate > @EndDate) 
    BEGIN 
        RAISERROR (N'Start date cannot be greater than end date',16,1) 
    END
    ELSE
    BEGIN 
        IF (@Notes IS NULL OR @Notes = '') 
        BEGIN 
            SELECT *
            FROM Expenses
            WHERE UserId = @UserId 
              AND [date] BETWEEN @StartDate AND @EndDate 
            ORDER BY [date] DESC 

            IF (@@ROWCOUNT = 0) 
            BEGIN 
                RAISERROR (N'No data found according to date match', 16, 1) 
            END 

            SELECT @Result = SUM(amount) 
            FROM Expenses 
            WHERE UserId = @UserId 
              AND [date] BETWEEN @StartDate AND @EndDate 
        END
        ELSE 
        BEGIN
            IF (@StartDate IS NULL OR @StartDate = '' OR @EndDate IS NULL OR @EndDate = '')
            BEGIN
                SELECT * 
                FROM expenses 
                WHERE UserId = @UserId 
                  AND Notes LIKE '%' + @notes + '%' 
                ORDER BY [date] DESC 

                SELECT @Result = SUM(amount) 
                FROM expenses 
                WHERE UserId = @UserId 
                  AND notes IN (SELECT notes 
                                FROM expenses 
                                WHERE notes LIKE '%' + @Notes + '%') 
            END
            ELSE
            BEGIN
                SELECT *
                FROM   Expenses  
                WHERE UserId=@UserId and notes LIKE '%' + @notes + '%'   and [date] BETWEEN @StartDate AND @EndDate 
                                 ORDER  BY [date] DESC 
                        IF( @@ROWCOUNT = 0 ) 
                        BEGIN 
                        RAISERROR(N'No matching record found',16,1) 
                        END 

                        SELECT @Result = Sum(amount) 
                        FROM   Expenses 
                        WHERE  Notes IN(SELECT Notes 
                              FROM   Expenses 
                             WHERE UserId=@UserId and  notes LIKE '%' + @Notes + '%') 
                        AND [date] BETWEEN @StartDate AND @EndDate 
            END
       END
    END 
 END

我的方法是这样的:

public IQueryable<Expenses> SearchExpenses(string UserId, string Notes, DateTime startFrom, DateTime endTo)
    
        List<Expenses> expenses = new List<Expenses>();
        using (DALC.GetConnection())
        
            DALC.Command("[spSearch]");
            DALC.cmd.Parameters.Add("@Notes", SqlDbType.NVarChar).Value = Notes;
            DALC.cmd.Parameters.Add("@StartDate", SqlDbType.DateTime).Value = startFrom;
            DALC.cmd.Parameters.Add("@EndDate", SqlDbType.DateTime).Value = endTo;
            DALC.cmd.Parameters.Add("@UserId", SqlDbType.NVarChar).Value = UserId;//I set my sp paremeter here but why error?
            //Is This the correct way to set an output parameter?
            SqlParameter howMuch = new SqlParameter("@Result", SqlDbType.Money);
            howMuch.Direction = ParameterDirection.Output;

            DALC.cmd.Parameters.Add(howMuch);
            DALC.cmd.ExecuteNonQuery();

            var result = Convert.ToDecimal(DALC.cmd.Parameters["@Result"]);

            using (SqlDataReader reader = DALC.cmd.ExecuteReader())
            
                reader.Read();
                Expenses expense = new Expenses
                
                    Id = Convert.ToInt32(reader["Id"]),
                    Desription = reader["Notes"].ToString(),
                    Date = Convert.ToDateTime(reader["Date"]),
                    IsCash = Convert.ToBoolean(reader["IsCash"]),
                    IsCard = Convert.ToBoolean(reader["IsCash"]),
                    SearchResultOut = result
                ;
                expenses.Add(expense);
            
        
        return expenses.AsQueryable();
    

错误:

System.Data.SqlClient.SqlException: '过程或函数'spSearch' 需要未提供的参数“@UserId”。

还有我的控制器:

[HttpPost]
    public IActionResult Search(string UserId, string notes, DateTime startFrom, DateTime endTo)
    
        UserId = GetUserId();
        context.SearchExpenses(UserId,notes,startFrom,endTo);            
        return View();
    

【问题讨论】:

你能出示你的 DALC 吗? 您是否将CommandType 设置为StoredProcedure @Sergey 是的,但是这不是我第一次使用这个class.. 到目前为止还没有问题.. @Zer0 肯定接受a look.. 【参考方案1】:

考虑以同样的方式创建所有的 sql 参数。

对于输出参数,您通过创建一个sql参数对象并设置方向来正确地做到这一点。

你应该尝试对其他参数做同样的事情(当然不要设置方向)

调试

你应该在执行之前放置一个断点,并在 cmd 对象的参数集合中添加一个监视以查看它的样子。

还要验证 cmd 对象是否属于存储过程类型。

如果全部检查完毕,打开 sql profiler,您应该能够看到通过 SQL profiler 传入的内容,并获取对存储过程及其所有参数的实际调用。

进行实际调用并在 SSMS 上运行,看看它做了什么。

这应该可以帮助您弄清楚。

当你得到结果时,语法是 var 结果 = Convert.ToDecimal(DALC.cmd.Parameters["@Result"].Value);

【讨论】:

是的,当有疑问时,我几乎总是检查数据库正在获取的确切命令。 SQL Profiler 在发现客户端难以调试的问题时非常有用。只需查看数据库正在尝试执行的生成的 SQL 语句,就可以节省大量时间。 是的。似乎多年来把一个人的头撞在众所周知的墙上有一些好处。 @Chuma 是的,我在发布这个问题之前已经完成了。This 是控制器断点,this 是我的手表 @Chuma 有人提到 cmd 对象是dalc class中的一个sp 您的 DALC 类似乎只是实例化、返回并允许执行标准命令对象。那么一步一步来,你有没有放过手表并验证了命令对象的设置及其参数集合?我在手表中看到的似乎是传递给函数的参数,而不是命令对象在执行之前的参数。探查器还显示什么?【参考方案2】:

在你的“DALC”类中的“Command”方法不能正常工作。 因为“命令”值在第一个字母中有特殊字符。

            var commandwithspecialCharacter = "[spSearch]";
            var cmd = new SqlCommand(commandwithspecialCharacter, new SqlConnection());
            if (commandwithspecialCharacter.StartsWith("sp")) 
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
            

            Console.WriteLine($"commandwithspecialCharacter==>cmd.CommandType");

第一个输出commandwithspecialCharachter==>文本

            var commandwithoutspecialCharacter = "spSearch";
            if (commandwithoutspecialCharacter.StartsWith("sp")) 
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
            

            Console.WriteLine($"commandwithoutspecialCharacter==>cmd.CommandType");

第二个输出commandwithoutspecialCharacter==>StoredProcedure

在startsWith方法中,结果为假,因为它有“[”字符。 StartsWith 控件与“Command”方法中的“[sp”控件一起正常工作。

【讨论】:

以上是关于如何在 ASP.NET Core MVC 中使用 ADO.NET 向存储过程添加参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.NET Core MVC 中使用依赖注入设计存储库模式?

如何在 ASP.NET Core MVC 中使用 ADO.NET 向存储过程添加参数?

如何使用 C# 在 ASP.NET Core 3.1 MVC 中使用会话变量

如何在 ASP.Net Core MVC 中使用 HTML 链接?

使用命令行如何在 ASP.NET 中首先从 DB 搭建脚手架 - 而不是 ASP.NET Core MVC?

使用 ASP.NET Core MVC,如何显示图像?