簡體   English   中英

在 C# 中編寫異步方法的最佳方法

[英]Best way to write an async method in C#

我有一個服務 object,它只是對 Microsoft SQL 數據庫之上的 EF Core 存儲庫執行查詢。 我正在嘗試將這些同步方法轉換為異步版本。 這是一種簡化方法的示例...

同步版本

public List<Contact> GetContacts()
{
    var ret = this.dbContext.Contacts.ToList(); 
    return ret;
}

我已經看到了幾種不創建 ASYNC 方法的方法......然后又......我看到了幾種不同的方法來說明你應該如何創建異步方法。 我陷入了以下兩種方法中的哪一種被認為是創建異步方法的“正確”或“最佳”方法之間。

異步版本 1

public Task<List<Contact>> GetContactsAsync()
{
    return Task.FromResult(this.dbContext.Contacts.ToList());
}

或者

異步版本 2

public async Task<List<Contact>> GetContactsAsync()
{
    List<Contact> ret = null;

    await Task.Run(()=>{
        ret = this.dbContext.Contacts.ToList(); 
    });

    return ret;
}

如果查詢包含多行邏輯,則可能使這進一步復雜化。

這是包含更多細節的相同方法。 如果您看到除了異步主題之外的問題,那么我很樂意在 PM 或其他內容中聽到它們,但是對於這個線程,我想專注於等待/異步的東西。

完整的同步代碼

public PaginationResult<Contact> GetContacts(PaginationSpec pagingSpec, List<Expression<Func<Models.Contact, bool>>> filter = null)
{
    IQueryable<Contact> query = ContactService.GetContactQuery(this.ctx);

    if (filter != null && filter.Count > 0)
    {
        foreach (var item in filter)
        {
            query = query.Where(item);
        }
    }

    // get the record count
    //
    var recordcount = query.Count();

    // get the pagination
    //
    var stage = pagingSpec.ApplyPagination<Models.Contact>(query);

    // Construct the paged result.
    // 
    var ret = new PaginationResult<Models.Contact>(recordcount, pagingSpec.CurrentPage, pagingSpec.PageSize, stage.ToList());

    return ret;
}

我將如何將其包裝在適當的語法中以使此方法異步? 我可以想到幾個方法...

異步版本 1

public Task<PaginationResult<Contact>> GetContactsAsync(PaginationSpec pagingSpec, List<Expression<Func<Models.Contact, bool>>> filter = null)
{
    return Task.FromResult(GetContacts(pagingSpec, filter)) // simply call the synchronous method ??;
}

或者

異步版本 2

public async Task<PaginationResult<Contact>> GetContactsAsync()
{
    PaginationResult<Contact> ret = null;

    await Task.Run(()=>{
    
        // Encapsulate the execution code within the RUN method ??
        //
        IQueryable<Contact> query = ContactService.GetContactQuery(this.ctx);

        if (filter != null && filter.Count > 0)
        {
            foreach (var item in filter)
            {
                query = query.Where(item);
            }
        }

        // get the record count
        //
        var recordcount = query.Count();

        // Apply the pagination spec to the query
        //
        var stage = pagingSpec.ApplyPagination<Models.Contact>(query);

        // Construct the esult.
        // 
        ret = new PaginationResult<Models.Contact>(recordcount, pagingSpec.CurrentPage, pagingSpec.PageSize, stage.ToList());
        });

    return ret;
}

這些中的任何一個都不合適,比一個或另一個更好,還是我錯過了第三個選項?

嘗試學習這個等待/異步的東西,任何幫助表示贊賞! 謝謝!

這兩者通常被認為是不正確的。

異步版本 1

這個問題是它實際上不是異步的。 它只是做相同的同步工作並將其包裝在Task<T>中。

異步版本 2

這個問題是從調用者的角度來看它是異步的,但在其實現中是同步的。 它只是使用Task.Run在線程池線程上運行同步代碼,因此它看起來是異步的。 這就是我所說的“假異步”,這對於桌面應用程序來說是可以的(不理想),當然不推薦用於 ASP.NET 應用程序。

更合適的解決方案如下所示:

public async Task<List<Contact>> GetContactsAsync()
{
    return await this.dbContext.Contacts.ToListAsync();
}

這是假設您有可用的ToListAsync方法。 注意與同步版本的相似之處:

public List<Contact> GetContacts()
{
    return this.dbContext.Contacts.ToList(); 
}

一般來說,您希望從最低級別開始,即實際執行IQueryable的代碼。 在這種情況下,我假設那是ToList ,但如果ToList是您自己的方法,那么它可能是該方法調用的任何方法。 一旦找到最低級別的代碼,然后將更改為使用await ,並讓 async 從那里增長。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM