是否可以在单个操作中执行许多存储过程?

时间:2012-04-19 09:36:24

标签: c# .net stored-procedures ado.net

我正在编写读取xml文件以更新数据库的代码。我得到大约500个xml文件,我想尽快处理它们。

所有数据库操作都是使用存储过程完成的。

每个xml文件都有大约35种不同的存储过程。

最初我写过像这样的代码

var cmd = new SqlCommand("EXEC UpdateTeamStats("+teamId+","+points+")");
cmd.CommandType = CommandType.Text;

但经过一些最佳实践后,我将其更改为

var cmd = new SqlCommand("UpdateTeamStats");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("teamId", 21);
cmd.Parameters.Add("points", 2);

因为从程序中调用了大量的存储过程,我意识到我必须进行较少数量的调用才能进行优化。

所以我想一起收集所有35个存储过程并一次执行它们。

存储过程因参数不同而不同,我不知道在上面的参数更改后收集和执行它们的方法。

我正在考虑调用一个巨大的存储过程并在该存储过程中调用另外35个,但我不擅长SQL,这会导致不必要的复杂性。

是否可以在C#中完全执行此操作?

或者是否有其他更好的方法来排队存储过程并快速运行它们

4 个答案:

答案 0 :(得分:1)

请从

下载Microsoft Applications数据块

http://www.microsoft.com/download/en/details.aspx?id=435

很好,但我该如何使用它?

这个包装类的用法非常简单。

DAC DC = new DAC();
DC.StoredProcedure = "nProc_InsertOrder";
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1" );
DC.Params.Add("@CustomerName", SqlDbType.VarChar, "test");
DAC.Commands.Add(DC);

DC = new DAC();
DC.StoredProcedure = "nProc_InsertOrderLineItems";
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1" );
DC.Params.Add("@OrderLineId", SqlDbType.VarChar, "A1");
DAC.Commands.Add(DC);

DC = new DAC();
DC.StoredProcedure = "nProc_InsertOrderLineItems";
DC.Params.Add("@OrderId", SqlDbType.VarChar, "Order1" );
DC.Params.Add("@OrderLineId", SqlDbType.VarChar, "A2");
DAC.Commands.Add(DC);

DC = new DAC();
DC.StoredProcedure = "nProc_CreateBill";
DC.Params.Add("@BillDate", SqlDbType.DateTime, DateTime.Now);
DC.Params.Add("@BillId", SqlDbType.VarChar, "Bill1");
DAC.Commands.Add(DC);
DAC.ExecuteBatch();

如果订单插入失败,则不应创建帐单。同样,如果订单项失败,则不应创建订单。我们通过ADO.Net在几行代码中实现了这一目标。

在这个例子中,在我们调用ExecuteBatch之前,我们实际上并没有插入记录,而是准备对象进行批量更新。

答案 1 :(得分:1)

最好的解决方案是编写一个带有传递的表值参数的存储过程,其中包含每个xml文件的所有参数列表。然后在这个存储过程中调用表值参数中每个记录的所有其他存储过程。

如果这不好,那么你可以使用类型为text而不是存储过程的SqlCommand,只需构建命令并执行它。您可以像现在一样使用参数,也可以只编写动态sql。

答案 2 :(得分:0)

您可以通过Command pattern创建CommandQueue并在命令上创建一个委托,该委托映射到您对存储过程的调用的任何要求;看起来像是:

public class CommandQueue
{
    private Connection _connexion = new Connection(); // Set this up somehow.

    // Other methods to handle the concurrency/ calling/ transaction etc.

    public Func<string, Dictionary<string, int>, bool> CallStoredProcedure = (procedureName, parameterValueMap) =>
    {
      cmd.Connection = GetConnexion();
      var cmd = new SqlCommand(procedureName);
      cmd.CommandType = CommandType.StoredProcedure;

      foreach (var parameterValueMapping in parameterValueMap)
      {
        cmd.Parameters.Add(parameterValueMapping.Key, parameterValueMapping.Value);
      }

      var success = cmd.ExecuteNonQuery();

      return success;
    }

    private Connection GetConnexion()
    {
      return _connexion;
    }
}

然后,设置CommandQueue,以便拥有一个线程池,您可以从中调用新线程上的委托,以便它们并行运行。

实际上,查看SQLCommand类,可以对其进行异步调用。因此,您应该能够异步调用每个存储过程,在每个存储过程完成时设置一个委托并将其全部包装在事务中,以便您可以在需要时通过在每个命令上调用Cancel()来回滚它们。我可能仍然使用CommandQueue来抽象出来,因为我建议你以后可能会改变它!

我认为我仍然使用CommandQueue上的委托来封装存储过程调用,这样就可以抽象出存储过程的详细信息,并使其他人更容易理解,也更容易理解保持。如果添加新的存储过程,或更改名称或其他内容,将会更容易。您可以设置静态列表,其中包含所有委托,或者包含必要存储过程详细信息的静态列表,并使用委托仅传入参数。

答案 3 :(得分:0)

就个人而言,根据我使用ADO.NET的经验,我认为使用单个 SqlConnection执行单独语句无论如何都没有问题。

这有利于利用.NET的惊人connection pooling,使您能够单独工作/自定义/与每个命令交互并使用共享连接(将连接代理量减少到一个微不足道的数量)。

我还想强调这里using条款的重要性,这些条款有助于妥善处理各种资源。

例如:

using (var conn = new SqlConnection("connection string"))
{
    using (var cmd = new SqlCommand())
    {
        cmd.Connection = conn;
        cmd.CommandType = CommandType.StoredProcedure;

        //ready to query 
        conn.Open();

        cmd.CommandText = "UpdateTeamStats";
        var teamIdParam = new SqlParameter("teamId", 21);
        var pointsParam = new SqlParameter("points", 2);

        cmd.Parameters.Add(teamIdParam);
        cmd.Parameters.Add(pointsParam);

        cmd.ExecuteNonQuery(); //OR if you're async cmd.ExecuteNonQueryAsync();

        //the rest of your executions

        conn.Close();
    }
}

如果我有一会儿请求,你可以使用像我的DbConnect那样的库,这会将上述内容减少到:

using (var db = new DbConnect("connection string"))
{
    db.SetSqlCommand("UpdateTeamStats");
    db.AddParameter("teamId", 21);
    db.AddParameter("points", 2);

    db.ExecuteNonQuery().Wait(); //OR if youre async await db.ExecuteNonQuery();

    db.ClearParameters();
    db.SetSqlCommand("some other proc");

    //rest of executions
}