您的位置:知识库 » 数据库

LINQ to SQL异步查询

作者: Jeffrey Zhao  来源: 博客园  发布时间: 2008-09-20 23:53  阅读: 6869 次  推荐: 0   原文链接   [收藏]  
摘要:当Web应用程序的吞吐量因为数据库操作的阻塞而受到影响的话,我们可是尝试使用异步数据库操作来进行优化。那么我们又该如何使用LINQ to SQL进行异步查询呢?
[1] 异步查询1
[2] 异步查询2

异步操作是提高Web应用程序吞吐量的重要手段,关于这方面的话题已经在前文《正确使用异步操作》中解释过了。对于大多数互联网应用来说,性能瓶颈数据库访问。换句话说,一个请求在数据库操作上所花的时间往往是最多的——并且占总时间的90%以上。因此,当Web应用程序的吞吐量因为数据库操作的阻塞而受到影响的话,我们可是尝试使用异步数据库操作来进行优化。

如果我们使用LINQ to SQL,在默认情况下是无法实现异步查询的,所有的操作都非常自然——异步是不自然的,因为它把连续的操作拆成了两段。如果理解了《在LINQ to SQL中使用Translate方法以及修改查询用SQL》一文中所提出的扩展方法,使用LINQ to SQL实现数据库的异步查询的方法应该就很容易想到了:借助SqlCommand对象来完成。

在.NET中实现一个此类异步操作自然是按照标准的APM(Asynchronous Programming Model,异步编程模型)来开发一对Begin和End方法。按照APM进行开发其实不是一件非常容易的事情,不过在.NET 2.0里,尤其是在.NET 3.5中的某个特性,开发此类功能就变得容易一些了——也就是说,这是个在.NET 1.x => .NET 2.0 => .NET 3.5的演变过程中都得到改进的特性,猜出来是什么了吗?没错,这个特性就是“匿名方法”。

匿名方法事实上基于委托,有了匿名方法这个特性,在一些本该使用委托的地方就可以直接定义一个函数了。这种做法在很多时候能够减少相当程度的代码量,尤其是本来很难省去的一些“条条框框”。例如,我们现在需要对一个Article列表按评论数量进行排序,并且在排序时可以指定升序或降序。如果没有匿名方法,我们一般会这么做:

public void SortByCommentCount(List<Article> articleList, bool ascending)
{
    // use the overloaded method: List<T>.Sort(Comparison<T> compare)
    ArticleComparison comparison = new ArticleComparison(ascending);
    articleList.Sort(new Comparison<Article>(comparison.Compare));
}

class ArticleComparison
{
    private bool m_ascending;

    public ArticleComparison(bool ascending)
    {
        this.m_ascending = ascending;
    }

    public int Compare(Article a, Article b)
    {
        return (a.CommentCount - b.CommentCount) * (this.m_ascending ? 1 : -1);
    }
}

我们使用接受Comparison<T>作为参数的List<T>.Sort方法重载,如果没有特别的要求,我们只需写一个静态方法就可以了——只要方法签名符合Comparision<Article>就行了。可惜在这里,我们需要写一个额外的类,因为我们需要访问一个额外的参数ascending,而这个参数不能在一个独立的Comparision<Article>委托中获得。于是我们写了一个ArticleComparison类,它唯一的目的就是封装ascending。如果我们每次使用Sort功能都要封装一个类的话编写的代码也就太多了。但是如果我们有了匿名方法之后:

public void SortByCommentCount
    (List<Article> articleList, bool ascending)
{
    articleList.Sort(delegate(Article a, Article b)
    {
        return (a.CommentCount - b.CommentCount) * (ascending ? 1 : -1);
    });
}

很明显,这种内联写法省去了额外的方法定义。而且更重要的是,匿名函数体内部能够访问到当前堆栈中的变量——其实这点才是最重要的。事实上,匿名方法的实现原理正是由编译器自动生成了一个封装类。有了匿名方法这个特性,我们就可以使用非常优雅的做法来实现一些轻量的委托。至于.NET 3.5里对于匿名方法的改进,主要在于引入了Lambda Expression:

public void SortByCommentCount(List<Article> articleList, bool ascending)
{
    articleList.Sort((a, b) => (a.CommentCount - b.CommentCount) * (ascending ? 1 : -1));
}

编译器会将现在的代码编译成之前与之前匿名方法相似的IL代码。.NET 3.5中LINQ的大量操作都以委托作为参数,因此也正是因为有了Lamda Expression到委托的转化,LINQ才能有如此威力。现在开发一个APM操作就方便多了。我们现在来构造一个扩展,将LINQ to SQL的查询异步化。首先是Begin方法(其中有些辅助方法以及参数的含义可以见之前的《在LINQ to SQL中使用Translate方法以及修改查询用SQL》一文):

public static IAsyncResult BeginExecuteQuery(
    this DataContext dataContext, IQueryable query, bool withNoLock,
    AsyncCallback callback, object asyncState)
{
    SqlCommand command = dataContext.GetCommand(query, withNoLock);
    dataContext.OpenConnection();

    AsyncResult<DbDataReader> asyncResult =
        new AsyncResult<DbDataReader>(asyncState);
    command.BeginExecuteReader(ar =>
    {
        try
        {
            asyncResult.Result = command.EndExecuteReader(ar);
        }
        catch (Exception e)
        {
            asyncResult.Exception = e;
        }
        finally
        {
            asyncResult.Complete();
            if (callback != null) callback(asyncResult);
        }
    }, null);

    return asyncResult;
}

在《正确使用异步操作》一文中我们已经谈过什么样的异步操作是“有效”的,从文章的内容我们不难得出一个结论,那就是我们无法使用托管代码“自行”实现适合I/O-Bound Operation的异步操作。我们为DataContext扩展的异步操作肯定是“封装”了ADO.NET所提供的异步特性来完成。很显然,我们需要获得一个DbDataReader,因此我们调用会调用SqlCommand对象的BeginExecuteReader方法,该方法的第一个参数是一个AsyncCallback委托类型的对象,当数据库的异步查询完成之后即会调用该委托,在这里使用匿名方法更合适。

[第1页][第2页]
0
0
标签:LINQ to SQL

数据库热门文章

    数据库最新文章

      最新新闻

        热门新闻