运行数据库查询而不等待结果

7
我需要调用一个做了很多工作的存储过程。
我希望能够“启动并忘记”,即在移动到下一条记录之前不等待存储过程的响应,因为这会减慢速度,而我需要快速移动。
在C#中,最佳的调用存储过程的方法是什么,不等待结果,返回成功状态,然后移动到下一条记录。
我想仅仅快速地遍历我的选择对象列表,并调用一些方法进行数据库调用,在移动到循环中的下一项之前不等待响应。
它使用的是C# 4.5。
我正在考虑使用类似于以下内容的东西:
Parallel.ForEach(sqlRecordset.AsEnumerable(), recordsetRow =>
{
  // get data for selection
  // call DB to save without waiting for response / success/failure status
}

但我不知道这是否是最好的方法。

有什么想法吗?

感谢您提前的帮助。


这取决于情况,这会创建1000个数据库连接吗?您需要能够设置一些管理控件以防止数据库连接耗尽。 - T McKeown
异步调用?https://dev59.com/XnA75IYBdhLWcg3wdIv7 - JPK
并发队列。http://msdn.microsoft.com/zh-cn/library/dd267265.aspx - huseyin tugrul buyukisik
3个回答

5

Parallel.ForEach并行但同步的 - 它会等待所有迭代完成。

您可以使用TPL(任务并行库)来实现您想要的功能:

foreach (var recordsetRow_doNotUse in sqlRecordset.AsEnumerable())
{
    var recordsetRow = recordsetRow_doNotUse;
    Task.Run(() => { 
        Console.WriteLine(recordsetRow);
        /* or do whatever you want with it here */ 
    });
}

Task.Run返回一个任务,因此如果你需要在所有任务完成时执行某些操作,可以将它们全部放入数组中,然后使用Task.WhenAll获取一个任务,该任务将在所有迭代完成时完成,而不会阻塞任何线程。

P.S. 我不知道你所说的C# 4.5是什么意思,可能是.NET 4.5,这是C# 5。我的示例代码不适用于早期版本的C#。


嗨,我使用command.BeginExecuteNonQuery创建了一个方法,但现在我的日志文件充满了错误=连接不支持MultipleActiveResultSets。我的数据类打开一个连接并保持它,以便不为每个请求生成新的连接-这是问题吗?实际上我正在使用.NET 4.0(刚刚检查过)和MS SQL 2005进行此DB。这是一个旧的工作DB,他们让我测试。有什么想法可以解决这个错误吗? - MonkeyMagix
我编辑了我的代码,使其能够与C# 4一起使用。从您收到的错误来看,似乎您无法将连接用于并行请求。每当创建Data Class对象时创建一个新连接可能是可以的,只需尝试一下,看看它是否会变得更慢。 - Ventsyslav Raikov
它能够与ASP.NET一起使用吗?我的意思是,即使ASP.NET请求完成后,它是否仍然保持运行状态?请求完成后,数据库连接被关闭并且存储过程被终止或回滚的可能性不存在吗? - David Ferenczy Rogožan
1
@DawidFerenczy - 是的,但最好使用QueueBackgroundWorkItem - Ventsyslav Raikov
@Bond 谢谢。您能否简要说明一下,为什么“Task”解决方案不好呢?(因为它非常简单且有效) - David Ferenczy Rogožan
如果你查看上面关于QueueBackgroundWorkItem的链接,你会找到如下解释: 与普通的线程池工作项不同的是,ASP.NET可以跟踪通过这个API注册的工作项的运行数量,并且ASP.NET运行时会尝试延迟应用程序域关闭,直到这些工作项执行完成。 - Ventsyslav Raikov

3
更简单的方法是使用ADO.NET:
var cmd = new SqlCommand("storeProcName", sqlConnection);
cmd.CommandType = CommandType.StoredProcedure;

cmd.BeginExecuteNonQuery();

在这种情况下,它如何解决问题?我很想了解。 - user3383479
这会调用SP,但不会等待结果。 - Stefano Altieri
酷,那么它只是向数据库发送命令,而不等待响应或返回记录集?如果存储过程确实返回记录集,会发生什么?它会被忽略吗?还是有一种方法可以解析它?虽然我不想对返回的记录集做任何事情,但我只是想为了“知识”而知道。谢谢。 - MonkeyMagix
如果您想要结果,可以使用返回值来调用cmd.EndExecuteNonQuery(或任何其他具有begin/end签名的方法)。 - Stefano Altieri
@StefanoAltieri,为什么我使用这个命令时似乎没有执行? - Albert Laure
它不是同步的。它在另一个线程上运行,可能很难调试。 - Stefano Altieri

0

既然您想要遍历每个记录,获取一些值并将其存储在数据库中,而不等待任何记录集,那么您可以像下面这样做;假设您正在使用Windows应用程序并了解线程:

foreach (DataRow row in DataTable.Rows) //you can use any enumerable object here
{
    string s = row.Fields("name").ToString();
    Thread t = new Thread(new ParameterizedThreadStart(SaveName));
    t.IsBackground = true;
    t.start(s);
}

public void SaveName(object s)
{
    //your code to save the value in 's'
}

这样,您就不必等待数据库响应,每个值都将被保存。线程将在完成保存和处理记录后立即被销毁。

希望这有所帮助。

编码愉快..!!


这会像上面的人说的那样产生大量与数据库的连接吗?这就是我担心的。因为我在工作中使用一个有重要网站的数据库进行测试,我不想让我的代码消耗所有可用的连接。在 SQL 2005+ 中,是否有一种方法可以在存储过程中触发另一个存储过程而不等待结果(而不必编写 C# CLR 存储过程)?这样我就可以以通常的循环方式调用我的存储过程,不会消耗大量连接,然后让存储过程启动并忘记。 - MonkeyMagix
你可以在类级别上声明一个全局连接对象,并将其用于调用多个存储过程实例。这将创建一个到数据库的连接,以执行存储过程的多个请求。希望这有所帮助。如果您有任何问题,请随时提问。 - Nayan Ambaliya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接