ASP MVC Core 3x асинхронный вызов базы данных — чего я не получаю?

#c# #sql-server #asp.net-mvc #asp.net-core #async-await

#c# #sql-сервер #asp.net-mvc #asp.net-core #асинхронный-ожидание

Вопрос:

Я надеюсь, что кто-нибудь сможет пролить свет на это, потому что я прыгаю между одним исключением и другим в VS, и мне это действительно надоело. Я читал статьи о том, как выполнять асинхронные вызовы, и видел примеры кода, но по какой-то причине я почему-то просто не получаю его в соответствии с VS.

У меня есть простой POCO, GetCompaniesResponse, который будет содержать набор компаний или ошибок в зависимости от того, что произойдет:

 public class GetCompanyResponse
{
    public List<Company> Companies { get; set; }
    public List<ErrorResult> Errors { get; set; }

    public GetCompanyResponse()
    {
        Companies = new List<Company>();
        Errors = new List<ErrorResult>();
    }
}
  

Все, что я пытаюсь сделать, это преобразовать синхронный вызов, который загружает компании из базы данных, в асинхронный вызов, чтобы освободить пользовательский интерфейс во время загрузки данных.

Вот метод, который я преобразовал:

     public async Task<GetCompanyResponse> GetCompaniesByQuery()
    {
        var response = new GetCompanyResponse();

        try
        {
            using (var conn = new SqlConnection(_configuration["Settings:DBConn"]))
            {
                var cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "SELECT TOP (1000) company_id, company_name, postcode FROM dbo.tbl_companies";
                conn.Open();
                var reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    response.Companies.Add(new Company
                    {
                        CompanyId = Convert.ToInt32(reader["company_id"]),
                        CompanyName = reader["company_name"].ToString(),
                        PostCode = reader["postcode"].ToString()
                    });
                }
            }
        }
        catch (Exception e)
        {
            response.Errors.Add(new ErrorResult
            {
                ErrorMessage = e.Message
            });
        }
        
        return response;
    }
  

И мое действие контроллера:

     public async Task<IActionResult> ByQuery()
    {
        var service = new TestService(_configuration);
        var response = await service.GetCompaniesByQuery();

        return View(response);
    }
  

Приведенный выше метод в VS просто продолжает говорить мне, что я не могу вернуть ответ, потому что он не может быть преобразован. Что я делаю не так?

 Severity    Code    Description Project File    Line    Suppression State
Error   CS0029  Cannot implicitly convert type 'TestApp.Models.GetCompanyResponse' to 'System.Threading.Tasks.Task<TestApp.Models.GetCompanyResponse>'  TestApp C:Working CopiesTestAppTestAppServicesTestService.cs   59  Active
  

Если я затем попытаюсь извлечь элемент задачи, который строго относится к свойству Companies ответа POCO, например:

     public async Task<IActionResult> ByQuery()
    {
        var service = new TestService(_configuration);
        var response = new GetCompanyResponse();
        response.Companies = (await service.GetCompaniesByQuery()).ToList();

        return View(response);
    }

    public async Task<IEnumerable<Company>> GetCompaniesByQuery()
    {
        var response = new List<Company>();

        try
        {
            using (var conn = new SqlConnection(_configuration["Settings:DBConn"]))
            {
                var cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "SELECT TOP (1000) company_id, company_name, postcode FROM dbo.tbl_companies";
                conn.Open();
                var reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    response.Add(new Company
                    {
                        CompanyId = Convert.ToInt32(reader["company_id"]),
                        CompanyName = reader["company_name"].ToString(),
                        PostCode = reader["postcode"].ToString()
                    });
                }
            }
        }
        catch (Exception e)
        {
            //not implemented yet
        }

        await Task.WhenAll((IEnumerable<Task>)response);
        
        return response;
    }
  

В то время как VS перестает жаловаться на это, я начинаю сталкиваться с недопустимыми ошибками приведения при запуске кода.

 InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[TestApp.Models.Company]' to type 'System.Collections.Generic.IEnumerable`1[System.Threading.Tasks.Task]'.
TestApp.Services.TestService.GetCompaniesByQuery() in TestService.cs
 
            await Task.WhenAll((IEnumerable<Task>)response);
TestApp.Controllers.HomeController.ByQuery() in HomeController.cs
 
            response.Companies = (await service.GetCompaniesByQuery()).ToList();
  

Для меня не имеет смысла, почему это является причиной, когда примеры, которые я видел, делают точно то же самое, что и выше. Чего я здесь не понимаю?

Комментарии:

1. Почему вы пишете асинхронный метод и не вызываете в нем какой-либо асинхронный код ? По крайней мере, используйте ExecuteReaderAsync .

2. «Приведенный выше метод в VS просто продолжает говорить мне, что я не могу вернуть ответ, потому что он не может быть преобразован». Пожалуйста, отредактируйте свой вопрос, чтобы он содержал фактическую ошибку компилятора , которую выдает эта строка, поскольку опубликованный вами код выглядит нормально.

3. «чтобы освободить пользовательский интерфейс» — это не то, как работают веб-приложения. Пользовательский интерфейс не собирается ничего делать в ответ на то, что происходит с сервером, пока не будет отправлен HTTP-ответ.

4. Смысл в том, чтобы использовать действие асинхронного контроллера, чтобы освободить потоки на сервере для обработки другой работы. Вы получите масштабируемость (при условии, что вы выполняете правильные асинхронные вызовы, такие как ExecuteReaderAsync), но при компромиссе с немного более медленной обработкой отдельных запросов. async Функция в C # и AJAX не имеют ничего общего друг с другом.

5. @DavidHirst в ASP.Net в контексте, целью метода async действия является освобождение текущего потока ThreadPool для обслуживания других HTTP-запросов, пока ожидается ввод-вывод. Ответ клиенту не отправляется до завершения метода.