首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何编写异步LINQ查询?

如何编写异步LINQ查询?
EN

Stack Overflow用户
提问于 2008-10-31 01:26:01
回答 4查看 48.1K关注 0票数 64

在我读了一大堆LINQ相关的东西后,我突然意识到没有一篇文章介绍如何编写异步LINQ查询。

假设我们使用LINQ to SQL,下面的语句是清晰的。但是,如果SQL数据库响应较慢,则使用此代码块的线程将受到阻碍。

代码语言:javascript
复制
var result = from item in Products where item.Price > 3 select item.Name;
foreach (var name in result)
{
    Console.WriteLine(name);
}

目前的LINQ查询规范似乎没有提供对此的支持。

有没有办法做异步编程LINQ?当结果准备好使用时,它的工作方式就像是有一个回调通知,而没有任何I/O阻塞延迟。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2008-10-31 02:05:54

虽然LINQ本身并没有这个功能,但是框架本身就有这个功能。您可以轻松地在30行左右的代码中执行自己的异步查询执行器……实际上,我只是为了你把这些东西组合在一起:)

编辑:通过写这篇文章,我发现了为什么他们没有实现它。它不能处理匿名类型,因为它们的作用域是局部的。因此,您无法定义您的回调函数。这是一件非常重要的事情,因为很多linq to sql的东西都是在select子句中创建它们的。下面的任何一个建议都会遭遇同样的命运,所以我仍然认为这个建议最容易使用!

编辑:唯一的解决方案是不使用匿名类型。您可以将回调声明为仅接受IEnumerable (无类型args),并使用反射来访问字段(ICK!!)。另一种方法是将回调声明为“动态”...哦..。等等..。这还没出来呢。:)这是另一个很好的例子,说明了如何使用动态。有些人可能会称之为滥用。

将以下代码放入您的实用程序库中:

代码语言:javascript
复制
public static class AsynchronousQueryExecutor
{
    public static void Call<T>(IEnumerable<T> query, Action<IEnumerable<T>> callback, Action<Exception> errorCallback)
    {
        Func<IEnumerable<T>, IEnumerable<T>> func =
            new Func<IEnumerable<T>, IEnumerable<T>>(InnerEnumerate<T>);
        IEnumerable<T> result = null;
        IAsyncResult ar = func.BeginInvoke(
                            query,
                            new AsyncCallback(delegate(IAsyncResult arr)
                            {
                                try
                                {
                                    result = ((Func<IEnumerable<T>, IEnumerable<T>>)((AsyncResult)arr).AsyncDelegate).EndInvoke(arr);
                                }
                                catch (Exception ex)
                                {
                                    if (errorCallback != null)
                                    {
                                        errorCallback(ex);
                                    }
                                    return;
                                }
                                //errors from inside here are the callbacks problem
                                //I think it would be confusing to report them
                                callback(result);
                            }),
                            null);
    }
    private static IEnumerable<T> InnerEnumerate<T>(IEnumerable<T> query)
    {
        foreach (var item in query) //the method hangs here while the query executes
        {
            yield return item;
        }
    }
}

你可以像这样使用它:

代码语言:javascript
复制
class Program
{

    public static void Main(string[] args)
    {
        //this could be your linq query
        var qry = TestSlowLoadingEnumerable();

        //We begin the call and give it our callback delegate
        //and a delegate to an error handler
        AsynchronousQueryExecutor.Call(qry, HandleResults, HandleError);

        Console.WriteLine("Call began on seperate thread, execution continued");
        Console.ReadLine();
    }

    public static void HandleResults(IEnumerable<int> results)
    {
        //the results are available in here
        foreach (var item in results)
        {
            Console.WriteLine(item);
        }
    }

    public static void HandleError(Exception ex)
    {
        Console.WriteLine("error");
    }

    //just a sample lazy loading enumerable
    public static IEnumerable<int> TestSlowLoadingEnumerable()
    {
        Thread.Sleep(5000);
        foreach (var i in new int[] { 1, 2, 3, 4, 5, 6 })
        {
            yield return i;
        }
    }

}

现在我要把这个放到我的博客上,非常方便。

票数 38
EN

Stack Overflow用户

发布于 2011-07-03 16:21:16

TheSoftwareJedi和ulrikb(又名user316318)的解决方案对任何LINQ类型都很好,但(正如Chris Moschini所指出的)不委托给利用Windows I/O完成端口的底层异步调用。

Wesley Bakker的Asynchronous DataContext帖子(由a blog post of Scott Hanselman触发)描述了使用sqlCommand.BeginExecuteReader/sqlCommand.EndExecuteReader,的LINQ to SQL类,它利用了Windows /O完成端口。

I/O completion ports为在多处理器系统上处理多个异步I/O请求提供了有效的线程模型。

票数 16
EN

Stack Overflow用户

发布于 2016-07-31 05:54:45

基于Michael Freidgeim's answer和前面提到的blog post from Scott Hansellman,以及可以使用async/await的事实,您可以实现可重用的ExecuteAsync<T>(...)方法,该方法异步执行底层的SqlCommand

代码语言:javascript
复制
protected static async Task<IEnumerable<T>> ExecuteAsync<T>(IQueryable<T> query,
    DataContext ctx,
    CancellationToken token = default(CancellationToken))
{
    var cmd = (SqlCommand)ctx.GetCommand(query);

    if (cmd.Connection.State == ConnectionState.Closed)
        await cmd.Connection.OpenAsync(token);
    var reader = await cmd.ExecuteReaderAsync(token);

    return ctx.Translate<T>(reader);
}

然后你可以像这样(重新)使用它:

代码语言:javascript
复制
public async Task WriteNamesToConsoleAsync(string connectionString, CancellationToken token = default(CancellationToken))
{
    using (var ctx = new DataContext(connectionString))
    {
        var query = from item in Products where item.Price > 3 select item.Name;
        var result = await ExecuteAsync(query, ctx, token);
        foreach (var name in result)
        {
            Console.WriteLine(name);
        }
    }
}
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/252355

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档