一尘不染

如何编写异步LINQ查询?

c#

阅读了一堆与LINQ相关的知识之后,我突然意识到没有文章介绍如何编写异步LINQ查询。

假设我们使用LINQ to SQL,下面的语句很清楚。但是,如果SQL数据库响应缓慢,则将阻止使用该代码块的线程。

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阻塞延迟时,它就像有一个回调通知。


阅读 437

收藏
2020-05-19

共1个答案

一尘不染

尽管LINQ本身并没有真正的功能,但是框架本身却可以…您可以轻松地在30行左右滚动自己的异步查询执行程序…实际上,我只是为您提供了这些功能:)

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

编辑:唯一的解决方案是不使用匿名类型。您可以将回调声明为仅采用IEnumerable(无类型args),并使用反射来访问字段(ICK
!!)。另一种方法是将回调声明为“动态” …哦…等等…还没有结束。:)这是如何使用动态的另一个不错的例子。有些人可能称其为虐待。

将此扔到您的实用程序库中:

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;
        }
    }
}

您可以这样使用它:

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;
        }
    }

}

现在将其放在我的博客上,非常方便。

2020-05-19