【问题标题】:Calling stored procedure for each row doesn't work为每一行调用存储过程不起作用
【发布时间】:2021-07-30 14:41:07
【问题描述】:

我在 foreach 循环中执行存储过程时遇到问题,这是我的代码:

 var members = await _context.Members
                .Where(x => !x.Deleted)
                .ToListAsync(cancellationToken);
                
using (SqlConnection con = new SqlConnection("server=localhost;database=ProductsDb;User=user;Password=password"))
{
    using (SqlCommand cmd = new SqlCommand("CalculateRolesForEachMember", con))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        // open connection ProductsDb database
        con.Open();

        foreach (var member in members)
        {
            cmd.Parameters.Add("@memberId", SqlDbType.UniqueIdentifier).Value = member.Id;
            cmd.Parameters.Add("@month", SqlDbType.Int).Value = (int)month;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = (int)year;


            SqlDataReader reader = cmd.ExecuteReader(); // ON SECOND ITERATION OF FOREACH CODE BREAKS HERE

            while (reader.Read())
            {
                member.MemberType = memberTypes.FirstOrDefault(x => x.Acronym == reader["Role"].ToString());
                member.ModifiedDate = DateTime.Now;
            }
            
            cmd.Parameters.Clear();

        }
        await _context.SaveChangesAsync(cancellationToken);
    }
}

这是我的例外:

System.InvalidOperationException: 已经有一个打开的 DataReader 与此 Connection 关联,必须先关闭。

在 Microsoft.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand 命令)

如何在不引入数据表的情况下使用此代码解决/修复此问题?

【问题讨论】:

  • 也许你应该在你的代码中添加reader.Close()...
  • 为什么你同时拥有ExecuteNonQuery()ExecuteReader() ?这基本上只执行每个存储过程 两次 - 一次而不期望返回结果集(对于 INSERTDELETEUPDATE 来说可以),第二次返回结果集。 .....这没什么用.....
  • @marc_s 它在我试验后就离开了,即使我删除了 cmd.ExecuteNonQuery();我仍然收到同样的错误..
  • @marc_s 检查我的更新伙伴,即使我删除了未使用的代码并应用了下面 cmets 中的一些代码,它仍然会中断。

标签: c# sql-server stored-procedures .net-core


【解决方案1】:

您需要使用using 块来处理您的读者。不要试图用.Close() 自己做,这不值得:

            using(SqlDataReader reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    member.MemberType = memberTypes.FirstOrDefault(x => x.Acronym == reader["Role"].ToString());
                    member.ModifiedDate = DateTime.Now;
                }
            }

【讨论】:

    【解决方案2】:

    如果我的想法是正确的,你需要改变一件事。目前您在 foreach 循环之前声明 cmd ,因此对于每个成员您正在更改现有命令。 我认为你应该从这个出发:

    using (SqlCommand cmd = new SqlCommand("CalculateRolesForEachMember", con))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        // open connection ProductsDb database
        con.Open();
    
        foreach (var member in members)
        {
            cmd.Parameters.Add("@memberId", SqlDbType.UniqueIdentifier).Value = member.Id;
            cmd.Parameters.Add("@month", SqlDbType.Int).Value = (int)month;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = (int)year;
    
            // IN SECOND ITERATION CODE BREAKS HERE
            cmd.ExecuteNonQuery();
    
            SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
    
            while (reader.Read())
            {
                member.MemberType = memberTypes.FirstOrDefault(x => x.Acronym == reader["Role"].ToString());
                member.ModifiedDate = DateTime.Now;
            }
        }
    
        await _context.SaveChangesAsync(cancellationToken);
    }
    

    到这里:

           foreach (var member in members)
            {
                using (SqlCommand cmd = new SqlCommand("CalculateRolesForEachMember", con))
                {
            cmd.CommandType = CommandType.StoredProcedure;
            // open connection ProductsDb database
            con.Open();
                cmd.Parameters.Add("@memberId", SqlDbType.UniqueIdentifier).Value = member.Id;
                cmd.Parameters.Add("@month", SqlDbType.Int).Value = (int)month;
                cmd.Parameters.Add("@year", SqlDbType.Int).Value = (int)year;
    
                cmd.ExecuteNonQuery();
    
                SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
    
                while (reader.Read())
                {
                    member.MemberType = memberTypes.FirstOrDefault(x => x.Acronym == reader["Role"].ToString());
                    member.ModifiedDate = DateTime.Now;
                }
                await _context.SaveChangesAsync(cancellationToken);
            }
    }
            
    

    在性能方面有更好的解决方案,这是肯定的,但它会完成它的工作。

    【讨论】:

      【解决方案3】:

      你必须调用 cmd.Parameters.Clear();每次命令执行后

           .....
      
      foreach (var member in members)
      {
          cmd.Parameters.Add("@memberId", SqlDbType.UniqueIdentifier).Value = member.Id;
          cmd.Parameters.Add("@month", SqlDbType.Int).Value = (int)month;
          cmd.Parameters.Add("@year", SqlDbType.Int).Value = (int)year;
      
          //  What do you need this for ????
          // cmd.ExecuteNonQuery();
      
          using (var reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
          {
              while (reader.Read())
              {
                  member.MemberType = memberTypes.FirstOrDefault(x => x.Acronym == reader["Role"].ToString());
                  member.ModifiedDate = DateTime.Now;
      
                  _context.Entry(member).State = EntityState.Modified;
      
              }
              reader.Close();
          }
      
          cmd.Parameters.Clear()
      }
      
      

      另一种方法是重用参数而不是调用 clear();

      .....
      
       cmd.Parameters.Add("@memberId", SqlDbType.UniqueIdentifier).Value =Guid.Empty;
       cmd.Parameters.Add("@month", SqlDbType.Int).Value = (int)month;
      cmd.Parameters.Add("@year", SqlDbType.Int).Value = (int)year;
      
      foreach (var member in members)
              {
                  
      cmd.Parameters["@memberId"].Value =  member.Id;
      
      .....
      
      }
      

      顺便说一句,你需要什么 cmd.ExecuteNonQuery();为了?

      【讨论】:

      • 定义参数一次,在循环之前 - 然后只在循环内设置它们的值,以避免必须一遍又一遍地定义相同的参数....
      • @Sergey 我在尝试解决这个问题时一直在尝试不同的变体,但即使我删除了 cmd.ExecuteNonQuery();我仍然有同样的问题.. 嗯
      • 您是否删除了 CommandBehavior.CloseConnection?
      • @Roxy'Pro 你是对的。我修复了我的代码。请再试一次。
      猜你喜欢
      • 1970-01-01
      • 2023-03-11
      • 2017-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      相关资源
      最近更新 更多