为每一行调用存储过程不起作用

Calling stored procedure for each row doesn't work

我在 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: There is already an open DataReader associated with this Connection which must be closed first.

at Microsoft.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)

如何在不引入数据表的情况下使用此代码solve/fix?

你必须调用 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();为了?

如果我的思路是正确的,你需要改变一件事。目前您在 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);
        }
}
        

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

你需要用 using 块来处理你的 reader。 不要试图用 .Close() 自己做,不值得:

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