一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

LINQ to SQL異步查詢

異步操作是提高Web應(yīng)用程序吞吐量的重要手段,關(guān)于這方面的話題已經(jīng)在前文《正確使用異步操作》中解釋過了。對于大多數(shù)互聯(lián)網(wǎng)應(yīng)用來說,性能瓶頸數(shù)據(jù)庫訪問。換句話說,一個請求在數(shù)據(jù)庫操作上所花的時間往往是最多的——并且占總時間的90%以上。因此,當Web應(yīng)用程序的吞吐量因為數(shù)據(jù)庫操作的阻塞而受到影響的話,我們可是嘗試使用異步數(shù)據(jù)庫操作來進行優(yōu)化。

如果我們使用LINQ to SQL,在默認情況下是無法實現(xiàn)異步查詢的,所有的操作都非常自然——異步是不自然的,因為它把連續(xù)的操作拆成了兩段。如果理解了《在LINQ to SQL中使用Translate方法以及修改查詢用SQL》一文中所提出的擴展方法,使用LINQ to SQL實現(xiàn)數(shù)據(jù)庫的異步查詢的方法應(yīng)該就很容易想到了:借助SqlCommand對象來完成。

在.NET中實現(xiàn)一個此類異步操作自然是按照標準的APM(Asynchronous Programming Model,異步編程模型)來開發(fā)一對Begin和End方法。按照APM進行開發(fā)其實不是一件非常容易的事情,不過在.NET 2.0里,尤其是在.NET 3.5中的某個特性,開發(fā)此類功能就變得容易一些了——也就是說,這是個在.NET 1.x => .NET 2.0 => .NET 3.5的演變過程中都得到改進的特性,猜出來是什么了嗎?沒錯,這個特性就是“匿名方法”。

匿名方法事實上基于委托,有了匿名方法這個特性,在一些本該使用委托的地方就可以直接定義一個函數(shù)了。這種做法在很多時候能夠減少相當程度的代碼量,尤其是本來很難省去的一些“條條框框”。例如,我們現(xiàn)在需要對一個Article列表按評論數(shù)量進行排序,并且在排序時可以指定升序或降序。如果沒有匿名方法,我們一般會這么做:

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>作為參數(shù)的List<T>.Sort方法重載,如果沒有特別的要求,我們只需寫一個靜態(tài)方法就可以了——只要方法簽名符合Comparision<Article>就行了。可惜在這里,我們需要寫一個額外的類,因為我們需要訪問一個額外的參數(shù)ascending,而這個參數(shù)不能在一個獨立的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);    });}

很明顯,這種內(nèi)聯(lián)寫法省去了額外的方法定義。而且更重要的是,匿名函數(shù)體內(nèi)部能夠訪問到當前堆棧中的變量——其實這點才是最重要的。事實上,匿名方法的實現(xiàn)原理正是由編譯器自動生成了一個封裝類。有了匿名方法這個特性,我們就可以使用非常優(yōu)雅的做法來實現(xiàn)一些輕量的委托。至于.NET 3.5里對于匿名方法的改進,主要在于引入了Lambda Expression:

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

編譯器會將現(xiàn)在的代碼編譯成之前與之前匿名方法相似的IL代碼。.NET 3.5中LINQ的大量操作都以委托作為參數(shù),因此也正是因為有了Lamda Expression到委托的轉(zhuǎn)化,LINQ才能有如此威力。現(xiàn)在開發(fā)一個APM操作就方便多了。我們現(xiàn)在來構(gòu)造一個擴展,將LINQ to SQL的查詢異步化。首先是Begin方法(其中有些輔助方法以及參數(shù)的含義可以見之前的《在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;}

在《正確使用異步操作》一文中我們已經(jīng)談過什么樣的異步操作是“有效”的,從文章的內(nèi)容我們不難得出一個結(jié)論,那就是我們無法使用托管代碼“自行”實現(xiàn)適合I/O-Bound Operation的異步操作。我們?yōu)镈ataContext擴展的異步操作肯定是“封裝”了ADO.NET所提供的異步特性來完成。很顯然,我們需要獲得一個DbDataReader,因此我們調(diào)用會調(diào)用SqlCommand對象的BeginExecuteReader方法,該方法的第一個參數(shù)是一個AsyncCallback委托類型的對象,當數(shù)據(jù)庫的異步查詢完成之后即會調(diào)用該委托,在這里使用匿名方法更合適。

 

這里的關(guān)鍵是用到了自己擴展的AsyncResult<T>類,該類除了標準的IAsyncResult實現(xiàn)之外,還釋放出一個System.Exception類型的Exception屬性和T類型的Result屬性。這兩個屬性的作用可以從上面的代碼中看出:Result的作用是保留異步操作的結(jié)果,Exception的作用自然是臨時保存調(diào)用SqlCommand.EndExecuteReader方法之后捕獲到的異常。這兩個臨時保留的對象都是為了在EndExecuteQuery方法中作進一步處理:

public static List<T> EndExecuteQuery<T>(    this DataContext dataContext, IAsyncResult ar){    AsyncResult<DbDataReader> asyncResult =         (AsyncResult<DbDataReader>)ar;    if (!asyncResult.IsCompleted)    {        asyncResult.AsyncWaitHandle.WaitOne();    }    if (asyncResult.Exception != null)    {        throw asyncResult.Exception;    }    using (DbDataReader reader = asyncResult.Result)    {        return dataContext.Translate<T>(reader).ToList();    }}

根據(jù)APM的規(guī)則,End方法將接受一個參數(shù),那就是Begin方法的返回值。因此我們可以在代碼中將其轉(zhuǎn)換成AsyncResult<DbDataReader>對象。按照規(guī)則,如果調(diào)用End方法時異步調(diào)用還沒有完成,則應(yīng)該阻塞當前線程直到異步操作完畢,因此我們的代碼調(diào)用了AsyncWaitHandle的WaitOne方法——當然,這里的寫法和我們的具體實現(xiàn)方式有關(guān)(詳見下文中AsyncResult<T>的實現(xiàn)方法)。然后檢查Exception屬性,如果不為空則表明在執(zhí)行數(shù)據(jù)庫的異步操作時拋出了一場,因此我們的End方法也將其繼續(xù)拋出。最后自然是根據(jù)獲得的DbDataReader對象,并借助DataContext的Translate方法生成對象。

至于AsyncResult<T>類型的實現(xiàn)方法非常簡單,我在這里將其簡單貼出,就不多作什么解釋了。不過有一點顯而易見,由于C# 3.0中的Automatic Property特性,代碼量比之前又能少了許多:

private class AsyncResult<T> : IAsyncResult{    public AsyncResult(object asyncState)    {        this.AsyncState = asyncState;        this.IsCompleted = false;        this.AsyncWaitHandle = new ManualResetEvent(false);    }    public object AsyncState { get; private set; }    public WaitHandle AsyncWaitHandle { get; private set; }    public bool CompletedSynchronously { get { return false; } }    public bool IsCompleted { get; private set; }    public void Complete()    {        this.IsCompleted = true;        (this.AsyncWaitHandle as ManualResetEvent).Set();    }    public T Result { get; set; }    public Exception Exception { get; set; }}

那么現(xiàn)在就來試用一下。在《正確使用異步操作》中也提到過,即使異步操作得到了IOCP支持,也必須正確使用這些異步操作才能真正得到效果。換句話說,我們必須在ASP.NET提供的幾個方面來使用異步功能。ASP.NET目前提供了三個可用于異步操作的地方:異步HttpModule,異步HttpHandler和異步Page,其中最常用的可能就是異步Page了。

public partial class AsyncPage : System.Web.UI.Page{    protected void Page_Load(object sender, EventArgs e)    {        this.AddOnPreRenderCompleteAsync(            new BeginEventHandler(BeginAsyncOperation),            new EndEventHandler(EndAsyncOperation));    }    private ItemDataContext m_dataContext;    private IAsyncResult BeginAsyncOperation(object sender, EventArgs e,        AsyncCallback cb, object state)    {        this.m_dataContext = new ItemDataContext();        var query = (from item in this.m_dataContext.Items                     orderby item.ItemID                     select item).Skip(10).Take(10);        return this.m_dataContext.BeginExecuteQuery(query, cb, state);    }    private void EndAsyncOperation(IAsyncResult ar)    {        using (this.m_dataContext.Connection)        {            this.rptItems.DataSource = this.m_dataContext.EndExecuteQuery(ar);            this.rptItems.DataBind();        }    }}

異步數(shù)據(jù)庫訪問已經(jīng)變得非常容易了,即使是LINQ to SQL也能較輕松地地獲得這方面的支持。不過在實際開發(fā)過程中我們可能還會遇到一點小問題:我們的應(yīng)用程序是分層的,而異步數(shù)據(jù)庫訪問是數(shù)據(jù)訪問層的能力。而如果我們要在表現(xiàn)層(HttpModule、HttpHandler、Page都屬于表現(xiàn)層)使用異步方法,就需要讓業(yè)務(wù)邏輯也提供一系列的支持——可能只是過渡,可能又是更多。這方面對于單線程的業(yè)務(wù)邏輯對象來說可能不是問題,但是在某些架構(gòu)中,業(yè)務(wù)邏輯對象可能會被多個線程(請求)同時訪問。但是既然要使用異步操作,就需要把一組Begin和End消息發(fā)送給同一個對象——在多線程共享一個業(yè)務(wù)邏輯對象的情況下,又該如何知道某個End消息應(yīng)該轉(zhuǎn)發(fā)給哪個下層對象呢?

這個話題我們下次再討論吧。

it知識庫LINQ to SQL異步查詢,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 欧美三级图片 | 国产精选在线视频 | 国产福利在线免费 | 午夜激情福利 | 久久久久久亚洲精品不卡 | 亚洲第一国产 | 都市激情亚洲色图 | 尹人久久大香找蕉综合影院 | 凹凸精品视频分类国产品免费 | 2021最新在线精品国产 | 成人在线观看午夜 | 免费大学生国产在线观看p 免费的成人a视频在线观看 | 婷婷亚洲激情 | 国产精品一区二区久久不卡 | 国产在线精品一区二区三区不卡 | 午夜视频一区二区三区 | 久久首页 | 巨大巨粗巨长 黑人长吊视频 | 337p人体 欧洲人体 亚 | www.亚洲一区二区三区 | 视频一区二区三区自拍 | 午夜免费观看_视频在线观看 | 羞羞色男人的天堂伊人久久 | 国产精品自拍一区 | 亚洲国产高清精品线久久 | 日本在线观看www | 精品一区二区三区四区 | 欧美特黄aaaaa | 国产第一区二区三区在线观看 | 天天狠天天透天天伊人 | 一区二区不卡在线观看 | 精品一区二区三区五区六区七区 | 亚洲第一页视频 | 国产成人亚洲精品2020 | 黄页网站在线播放 | 国产欧美日韩不卡一区二区三区 | 国产精亚洲视频 | 亚洲最大视频网 | 亚洲视频一区网站 | 国产精品亚洲一区二区三区 | 精品一区二区三区18 |