简体   繁体   English

在 C# 中编写异步方法的最佳方法

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

I have a service object that simply executes queries against an EF Core repository on top of a Microsoft SQL Database.我有一个服务 object,它只是对 Microsoft SQL 数据库之上的 EF Core 存储库执行查询。 I am trying to convert these synchronous methods to async versions.我正在尝试将这些同步方法转换为异步版本。 Here's an example of one simplified method...这是一种简化方法的示例...

Synchronous Version同步版本

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

I have seen several way to NOT create ASYNC methods... then again... I've seen a couple different ways on how you SHOULD create async methods.我已经看到了几种不创建 ASYNC 方法的方法......然后又......我看到了几种不同的方法来说明你应该如何创建异步方法。 I am caught between which of the two methods below is considered the "right" or "best" way to create an async methods.我陷入了以下两种方法中的哪一种被认为是创建异步方法的“正确”或“最佳”方法之间。

Async Version 1异步版本 1

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

OR或者

Async Version 2异步版本 2

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

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

    return ret;
}

Potentially complicating this further is, if the query contains several lines of logic.如果查询包含多行逻辑,则可能使这进一步复杂化。

Here is the same method with more of the detail included.这是包含更多细节的相同方法。 If you see issues aside from the async topic, then I would love to hear about them in a PM or something, but for this thread I want to focus on the await/async stuff.如果您看到除了异步主题之外的问题,那么我很乐意在 PM 或其他内容中听到它们,但是对于这个线程,我想专注于等待/异步的东西。

Complete Synchronous Code完整的同步代码

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

How would I wrap this in the appropriate syntax to make this method asynchronous?我将如何将其包装在适当的语法中以使此方法异步? I can think of a couple of ways...我可以想到几个方法...

Async Version 1异步版本 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 ??;
}

OR或者

Async Version 2异步版本 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;
}

Are either of these NOT appropriate, BETTER than one or the other or am I missing a 3rd option?这些中的任何一个都不合适,比一个或另一个更好,还是我错过了第三个选项?

Trying to learn this await/async stuff and any help is appreciated!尝试学习这个等待/异步的东西,任何帮助表示赞赏! Thanks!谢谢!

Both of those are generally considered incorrect.这两者通常被认为是不正确的。

Async Version 1异步版本 1

The problem with this one is that it is not actually asynchronous.这个问题是它实际上不是异步的。 It's just doing the same synchronous work and wrapping it in a Task<T> .它只是做相同的同步工作并将其包装在Task<T>中。

Async Version 2异步版本 2

The problem with this one is that it is asynchronous from the perspective of the caller, but synchronous in its implementation.这个问题是从调用者的角度来看它是异步的,但在其实现中是同步的。 It's just using Task.Run to run synchronous code on a thread pool thread so it appears asynchronous.它只是使用Task.Run在线程池线程上运行同步代码,因此它看起来是异步的。 This is what I call "fake asynchronous", which is OK (not ideal) for desktop apps, and certainly not recommended for ASP.NET apps.这就是我所说的“假异步”,这对于桌面应用程序来说是可以的(不理想),当然不推荐用于 ASP.NET 应用程序。

A more proper solution would look like this:更合适的解决方案如下所示:

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

This is assuming you have a ToListAsync method available.这是假设您有可用的ToListAsync方法。 Note the similarity to the synchronous version:注意与同步版本的相似之处:

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

Generally speaking, you want to start at the lowest level, ie, the code that actually executes the IQueryable .一般来说,您希望从最低级别开始,即实际执行IQueryable的代码。 In this case, I'm assuming that's ToList , but if ToList is your own method, then it might be whatever that method calls.在这种情况下,我假设那是ToList ,但如果ToList是您自己的方法,那么它可能是该方法调用的任何方法。 Once you find the lowest-level code, then change that to use await , and let the async grow from there.一旦找到最低级别的代码,然后将更改为使用await ,并让 async 从那里增长。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM