当前位置:  开发笔记 > 编程语言 > 正文

SqlException:过程或函数指定了太多参数

如何解决《SqlException:过程或函数指定了太多参数》经验,为你挑选了1个好方法。

我对SQL的经验很少,我有一种方法可以访问数据库并将数据添加到几个不同的表中。我认为我的存储过程是正确的。这是我遇到问题的方法。

我正在创建一个Web API端点,该端点是PUT,它应该能够向多个会话添加多个假期和日期并更新数据库。

这是我目前所拥有的,我需要做些什么,以免出现异常?

private void SaveGlobalOrderDays(List sessionList, List selectedOrderHolidays, List selectedHolidays, List orderDays, List noOrderDays)
{
    try
    {
        using (SqlCommand cmd = new SqlCommand())
        using (SqlConnection connection = new SqlConnection(ConnectionString))
        {
            cmd.CommandTimeout = 600;
            cmd.CommandText = "[dbo].[SaveGlobalOrderDays]"; 
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = connection;

            foreach (SessionInfoList session in sessionList)
            {
                cmd.Parameters.Add("@SessionId", SqlDbType.Int).Value = session.SessionID;

                if (selectedOrderHolidays.Count > 0)
                {
                    foreach (Holiday holiday in selectedOrderHolidays)
                    {
                        string orderHolidays = string.Join(",", holiday.Name).Trim();
                        cmd.Parameters.Add("@HolidayName", SqlDbType.NVarChar).Value = orderHolidays; 
                    }                                
                }

                if (selectedHolidays.Count > 0)
                {
                    foreach (Holiday holiday in selectedHolidays)
                    {
                        string noOrderHolidays = string.Join(",", holiday.Name).Trim();
                        cmd.Parameters.Add("@SelectedHolidayName", SqlDbType.NVarChar).Value = noOrderHolidays; 
                    }                                
                }

                if (orderDays.Count > 0)
                {
                    foreach (string order in orderDays)
                    {
                        string od = string.Join(",",order).Trim();
                        cmd.Parameters.Add("@OrderDays", SqlDbType.NVarChar).Value = od; 
                    }                                
                }

                if (noOrderDays.Count > 0)
                {
                    foreach (string noOrder in noOrderDays)
                    {
                        string nod = string.Join(",",noOrder).Trim();
                        cmd.Parameters.Add("@NoOrderDays", SqlDbType.NVarChar).Value = nod; 
                    }                                
                }                                             
            }

            foreach (SqlParameter parameter in cmd.Parameters)
            {
                if (parameter.Value == null)
                    parameter.Value = DBNull.Value;
            }

            connection.Open();

            cmd.ExecuteNonQuery();

            cmd.Dispose();
        }
    }
    catch (Exception ex)
    {
        string message = "Failed to save global order info";
        // Logger.Error(LogSource, "SaveGlobalOrderDays", string.Empty, message, string.Empty, ex);
        throw new Exception(message, ex);
    }
}

存储过程:

IF OBJECT_ID('[dbo].[SaveGlobalOrderDays]') IS NOT NULL
     DROP PROCEDURE [dbo].[SaveGlobalOrderDays]
GO

CREATE PROCEDURE [dbo].[SaveGlobalOrderDays]
    @SessionId INT,
    @HolidayName NVARCHAR(MAX),
    @SelectedHolidayName NVARCHAR(MAX),
    @OrderDays NVARCHAR(MAX),
    @NoOrderDays NVARCHAR(MAX)
WITH ENCRYPTION
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE [cfgSchedule]
    SET [cfgSchedule].[SessionId] = @SessionId,
        [OrderDays] = @OrderDays,
        [NoOrderDays] = @NoOrderDays
    FROM [cfgSchedule],[SessionHolidayMapping],[SessionOrderHolidayMapping]
    WHERE [cfgSchedule].[SessionId] = [SessionHolidayMapping].[SessionId]
      AND [cfgSchedule].[SessionId] = [SessionOrderHolidayMapping].[SessionId]
      AND [cfgSchedule].[SessionId] = @SessionId

    UPDATE [SessionHolidayMapping]
    SET [SessionHolidayMapping].[HolidayName] = @SelectedHolidayName
    FROM [cfgSchedule],[SessionHolidayMapping],[SessionOrderHolidayMapping]
    WHERE [cfgSchedule].[SessionId] = [SessionHolidayMapping].[SessionId]
      AND [cfgSchedule].[SessionId] = [SessionOrderHolidayMapping].[SessionId]
      AND [cfgSchedule].[SessionId] = @SessionId

    UPDATE [SessionOrderHolidayMapping]
    SET [SessionOrderHolidayMapping].[HolidayName] = @HolidayName
    FROM [cfgSchedule],[SessionHolidayMapping],[SessionOrderHolidayMapping]
    WHERE [cfgSchedule].[SessionId] = [SessionHolidayMapping].[SessionId]
      AND [cfgSchedule].[SessionId] = [SessionOrderHolidayMapping].[SessionId]
      AND [cfgSchedule].[SessionId] = @SessionId
END
GO  

Joel Coehoor.. 6

我们有这个循环:

foreach (SessionInfoList session in sessionList)
{

在循环中,我们有以下参数:

cmd.Parameters.Add("@SessionId", SqlDbType.Int).Value = session.SessionID;

并有条件地将其他条件包含在循环中:

cmd.Parameters.Add("@NoOrderDays", SqlDbType.NVarChar).Value = nod; 
cmd.Parameters.Add("@HolidayName", SqlDbType.NVarChar).Value = orderHolidays; 
cmd.Parameters.Add("@SelectedHolidayName", SqlDbType.NVarChar).Value = noOrderHolidays; 
cmd.Parameters.Add("@OrderDays", SqlDbType.NVarChar).Value = od; 

这些循环尝试在每次迭代时新项目重新添加到Parameters集合,这意味着从任何一个参数第一次到达它的第二次迭代开始,参数数量就会错误。

此外,我们在循环的底部执行此操作:

connection.Open();
cmd.ExecuteNonQuery();
cmd.Dispose();

这样的紧密循环是我们不必每次都重新创建并重新打开连接的情况。我们开工吧:

private void SaveGlobalOrderDays(IEnumerable sessionList, IEnumerable selectedOrderHolidays, IEnumerable selectedHolidays, IEnumerable orderDays, IEnumerable noOrderDays)
{
    try
    {
        using (SqlConnection connection = new SqlConnection(ConnectionString))
        using (SqlCommand cmd = new SqlCommand("[dbo].[SaveGlobalOrderDays]", connection))
        {
            cmd.CommandTimeout = 600;
            cmd.CommandType = CommandType.StoredProcedure;

            //Set up parameter placeholders once, before any looping starts.
            // From here on out we'll only ever set their .Value properties.
            cmd.Parameters.Add("@SessionId", SqlDbType.Int);
            //Add a Length to these!
            // If the target columns really are nvarchar(max), use -1
            cmd.Parameters.Add("@HolidayName", SqlDbType.NVarChar, -1); 
            cmd.Parameters.Add("@SelectedHolidayName", SqlDbType.NVarChar, -1);
            cmd.Parameters.Add("@OrderDays", SqlDbType.NVarChar, -1);
            cmd.Parameters.Add("@NoOrderDays", SqlDbType.NVarChar, -1);


            //Open the connection just once, immediately before beginning of the loop.
            // The using block will ensure it is closed later.
            connection.Open(); 
            foreach (SessionInfoList session in sessionList)
            {
                cmd.Parameters["@SessionId"].Value = session.SessionID;

                cmd.Paramters["@HolidayName"].Value = DBNull.Value;
                string joinedNames = string.Join(",", selectedOrderHolidays.Select(h => h.Name.Trim()));
                if (!string.IsNullOrEmpty(joinedNames))
                    cmd.Paramters["@HolidayName"].Value = joinedNames;

                cmd.Paramters["@SelectedHolidayName"].Value = DBNull.Value;
                joinedNames = string.Join(",", selectedHolidays.Select(h => h.Name.Trim()));
                if (!string.IsNullOrEmpty(orderHolidays))
                    cmd.Paramters["@SelectedHolidayName"].Value = joinedNames;

                cmd.Paramters["@OrderDays"].Value = DBNull.Value;
                joinedNames = string.Join(",", orderDays);
                if (!string.IsNullOrEmpty(joinedNames))
                    cmd.Paramters["@OrderDays"].Value = joinedNames;

                cmd.Paramters["@NoOrderDays"].Value = DBNull.Value;
                joinedNames = string.Join(",", noOrderDays);
                if (!string.IsNullOrEmpty(joinedNames))
                    cmd.Paramters["@NoOrderDays"].Value = joinedNames;

                //Now there's no need to Open() or Dispose() anything at this point.
                cmd.ExecuteNonQuery();
            }
        }
        // Consider using a separate Try/Catch inside the loop.
        // ... unless you want one failure to abort everything part way through, with some updates completed and some not.
        catch (Exception ex)
        {
            string message = "Failed to save global order info";
            // Logger.Error(LogSource, "SaveGlobalOrderDays", string.Empty, message, string.Empty, ex);
            throw new Exception(message, ex);
        }
    }

最后,这看起来像是在各个列中设置逗号分隔的值。这真的糟糕的架构设计!切勿将逗号分隔的数据放在单个列中。将这些列放入其他表中可能会做得更好。



1> Joel Coehoor..:

我们有这个循环:

foreach (SessionInfoList session in sessionList)
{

在循环中,我们有以下参数:

cmd.Parameters.Add("@SessionId", SqlDbType.Int).Value = session.SessionID;

并有条件地将其他条件包含在循环中:

cmd.Parameters.Add("@NoOrderDays", SqlDbType.NVarChar).Value = nod; 
cmd.Parameters.Add("@HolidayName", SqlDbType.NVarChar).Value = orderHolidays; 
cmd.Parameters.Add("@SelectedHolidayName", SqlDbType.NVarChar).Value = noOrderHolidays; 
cmd.Parameters.Add("@OrderDays", SqlDbType.NVarChar).Value = od; 

这些循环尝试在每次迭代时新项目重新添加到Parameters集合,这意味着从任何一个参数第一次到达它的第二次迭代开始,参数数量就会错误。

此外,我们在循环的底部执行此操作:

connection.Open();
cmd.ExecuteNonQuery();
cmd.Dispose();

这样的紧密循环是我们不必每次都重新创建并重新打开连接的情况。我们开工吧:

private void SaveGlobalOrderDays(IEnumerable sessionList, IEnumerable selectedOrderHolidays, IEnumerable selectedHolidays, IEnumerable orderDays, IEnumerable noOrderDays)
{
    try
    {
        using (SqlConnection connection = new SqlConnection(ConnectionString))
        using (SqlCommand cmd = new SqlCommand("[dbo].[SaveGlobalOrderDays]", connection))
        {
            cmd.CommandTimeout = 600;
            cmd.CommandType = CommandType.StoredProcedure;

            //Set up parameter placeholders once, before any looping starts.
            // From here on out we'll only ever set their .Value properties.
            cmd.Parameters.Add("@SessionId", SqlDbType.Int);
            //Add a Length to these!
            // If the target columns really are nvarchar(max), use -1
            cmd.Parameters.Add("@HolidayName", SqlDbType.NVarChar, -1); 
            cmd.Parameters.Add("@SelectedHolidayName", SqlDbType.NVarChar, -1);
            cmd.Parameters.Add("@OrderDays", SqlDbType.NVarChar, -1);
            cmd.Parameters.Add("@NoOrderDays", SqlDbType.NVarChar, -1);


            //Open the connection just once, immediately before beginning of the loop.
            // The using block will ensure it is closed later.
            connection.Open(); 
            foreach (SessionInfoList session in sessionList)
            {
                cmd.Parameters["@SessionId"].Value = session.SessionID;

                cmd.Paramters["@HolidayName"].Value = DBNull.Value;
                string joinedNames = string.Join(",", selectedOrderHolidays.Select(h => h.Name.Trim()));
                if (!string.IsNullOrEmpty(joinedNames))
                    cmd.Paramters["@HolidayName"].Value = joinedNames;

                cmd.Paramters["@SelectedHolidayName"].Value = DBNull.Value;
                joinedNames = string.Join(",", selectedHolidays.Select(h => h.Name.Trim()));
                if (!string.IsNullOrEmpty(orderHolidays))
                    cmd.Paramters["@SelectedHolidayName"].Value = joinedNames;

                cmd.Paramters["@OrderDays"].Value = DBNull.Value;
                joinedNames = string.Join(",", orderDays);
                if (!string.IsNullOrEmpty(joinedNames))
                    cmd.Paramters["@OrderDays"].Value = joinedNames;

                cmd.Paramters["@NoOrderDays"].Value = DBNull.Value;
                joinedNames = string.Join(",", noOrderDays);
                if (!string.IsNullOrEmpty(joinedNames))
                    cmd.Paramters["@NoOrderDays"].Value = joinedNames;

                //Now there's no need to Open() or Dispose() anything at this point.
                cmd.ExecuteNonQuery();
            }
        }
        // Consider using a separate Try/Catch inside the loop.
        // ... unless you want one failure to abort everything part way through, with some updates completed and some not.
        catch (Exception ex)
        {
            string message = "Failed to save global order info";
            // Logger.Error(LogSource, "SaveGlobalOrderDays", string.Empty, message, string.Empty, ex);
            throw new Exception(message, ex);
        }
    }

最后,这看起来像是在各个列中设置逗号分隔的值。这真的糟糕的架构设计!切勿将逗号分隔的数据放在单个列中。将这些列放入其他表中可能会做得更好。

推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有