是否可以在单个操作中执行多个存储过程
本文关键字:执行 存储过程 操作 单个 是否 | 更新日期: 2023-09-27 18:31:49
我正在编码以读取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# 中执行此操作?
或者是否有其他更好的方法来排队存储过程并快速运行它们
请从以下位置下载Microsoft应用程序数据块
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 之前,我们实际上并不是在插入记录,而是在准备对象以进行批量更新。
最好的解决方案 imo 是编写一个存储过程,其中包含传入的表值参数,其中包含每个 xml 文件的所有参数的列表。然后,在此存储的 proc 中,为表值参数中的每个记录调用所有其他存储的过程。
如果这不行,则可以使用文本类型的 SqlCommand 而不是存储过程,并随时构建命令并执行它。您可以像现在一样使用参数,也可以只编写动态 sql。
您可以通过命令模式创建一个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
上封装带有委托的存储过程调用,以便抽象出存储过程的详细信息,并使该过程更容易被其他人理解,也更容易维护。 如果您添加新的存储进程,或者更改名称或其他内容,则会容易得多。 可以设置可以包含所有委托的静态列表,也可以设置包含必要存储过程详细信息的静态列表,并使用委托仅传入参数。
就个人而言,根据我使用 ADO.NET 的经验,我认为使用单个SqlConnection
将它们作为单独的语句执行没有任何问题。
这有杠杆的好处。NET 惊人的连接池使您能够单独工作/自定义/与每个命令交互,并骑乘共享连接(将连接代理的数量减少到微不足道的数量)。
我还想强调这里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
}