首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

LINQ to SQL 非同步查詢實作:基於 APM 模式的完整擴充方案

     編輯:關於.NET

Web 應用效能瓶頸多半在資料庫存取(佔總時間 90%+)。LINQ to SQL 預設不支援非同步查詢,但透過擴充方法封裝 ADO.NET 的 BeginExecuteReader/EndExecuteReader,即可輕鬆實作非同步資料存取,配合 ASP.NET 非同步頁面機制顯著提升吞吐量。

核心架構:APM 模式 + 匿名方法 / Lambda

.NET 1.x → 2.0 → 3.5 演進中,「匿名方法」與「Lambda Expression」極大簡化了非同步程式設計模式 (APM) 的實作:

  • 匿名方法可內聯定義回呼委派,自動捕獲區域變數(編譯器自動產生封裝類別)。
  • Lambda 進一步精簡語法,成為 LINQ 委派參數的標準寫法。

擴充方法實作

1. 自訂 AsyncResult<T> 類別

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

2. BeginExecuteQuery 擴充方法

public static IAsyncResult BeginExecuteQuery(
    this DataContext dataContext,
    IQueryable query,
    bool withNoLock,
    AsyncCallback callback,
    object asyncState)
{
    SqlCommand cmd = dataContext.GetCommand(query, withNoLock);
    dataContext.OpenConnection();
    var asyncResult = new AsyncResult<DbDataReader>(asyncState);
    cmd.BeginExecuteReader(ar =>
    {
        try
        {
            asyncResult.Result = cmd.EndExecuteReader(ar);
        }
        catch (Exception ex)
        {
            asyncResult.Exception = ex;
        }
        finally
        {
            asyncResult.Complete();
            callback?.Invoke(asyncResult);
        }
    }, null);
    return asyncResult;
}

3. EndExecuteQuery<T> 擴充方法

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

ASP.NET 非同步頁面整合範例

public partial class AsyncPage : System.Web.UI.Page
{
    private ItemDataContext _dataContext;

    protected void Page_Load(object sender, EventArgs e)
    {
        AddOnPreRenderCompleteAsync(
            new BeginEventHandler(BeginAsyncOperation),
            new EndEventHandler(EndAsyncOperation));
    }

    private IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
        AsyncCallback cb, object state)
    {
        _dataContext = new ItemDataContext();
        var query = (from item in _dataContext.Items
                     orderby item.ItemID
                     select item).Skip(10).Take(10);
        return _dataContext.BeginExecuteQuery(query, cb, state);
    }

    private void EndAsyncOperation(IAsyncResult ar)
    {
        using (_dataContext.Connection)
        {
            rptItems.DataSource = _dataContext.EndExecuteQuery(ar);
            rptItems.DataBind();
        }
    }
}

架構考量:分層與多執行緒共享

非同步資料存取屬資料存取層能力,若表現層(Page/Handler/Module)使用非同步,業務邏輯層 (BLL) 亦需提供對應的 Begin/End 配對。在多執行緒共享 BLL 實例時,需設計機制確保 End 訊息正確路由至對應的下層物件(例如使用狀態物件或 AsyncState 關聯),這將在後續文章探討。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved