c# – ASP.NET MVC5 – Async Methods in Controller

Question:

I'm developing an MVC layer for an ASP.NET system initially written in WebForms. It doesn't use EntityFramework, so a good part of the system I had to come up with a homemade solution.

So I can use Ajax asynchronous methods with MVC5 Controllers, I wrote the following method as an example:

[Authorize]
public async Task<JsonResult> IndexAsync()
{
    var pessoas = new Pessoas(GeneralSettings.DataBaseConnection)
        .Selecionar()
        .Select(p => new { NOME = p.Nome, CPF = p.Cpf, FONE = p.TelefoneResidencial, CELULAR = p.TelefoneCelular, DEPARTAMENTO = "Teste", EMAIL = p.Email })
        .Take(10);
    return Json(pessoas, JsonRequestBehavior.AllowGet);
}

This method is not asynchronous because return Json(... executes synchronously. The following message appears:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(…)' to do CPU-bound work on a background thread.

How should I change the method so that the execution is asynchronous?

EDIT

@dcastro asked for the Selecionar() method, which is reproduced below:

public override IEnumerable<Pessoa> Selecionar(IEnumerable<Operador> operadores)
{
    using (var obj = new Database())
    {
        var sSql =
            "select p.ID_PESSOA, p.NOME_COMPLETO, p.APELIDO, p.EMAIL_PESSOAL, p.NOME_PAI, p.NOME_MAE, p.SEXO, p.CPF, p.RG, p.ORGAO_RG, " +
            " p.EMISSAO_RG, p.DATA_NASC, p.LOCAL_NASC, p.ENDERECO, p.NUMERO, p.COMPLEMENTO, p.BAIRRO, p.CIDADE, p.ESTADO, p.DDD_FONE_RES, " +
            " p.FONE_RES, p.DDD_FONE_CEL, p.CELULAR, p.ID_BANCO_TALENTOS, p.ESTADO_CIVIL, p.ID_NACIONALIDADE, p.DEFICIENTE, p.TAMANHO_SAPATO, " +
            " p.TAMANHO_CAMISETA, p.ALERGIA, p.NOME_CONJUGE, p.DDD_TEL_EMERGENCIA, p.TEL_EMERGENCIA, p.DDD_CEL_EMERGENCIA, p.CEL_EMERGENCIA, " +
            " p.CONTATO_EMERGENCIA, p.ID_FORMACAO_ACADEMICA, p.PARENTESCO, p.FOTO_PESSOA, p.RI, p.TITULO_ELEITOR, p.ZONA, p.SECAO, " +
            " p.CART_TRABALHO, p.SERIE, p.PIS, p.CPF_CONJUGE, p.LINKEDIN, p.FACEBOOK, p.TWITTER, p.ID_ETNIA, p.CEP " +
            " from PESSOAS p ";

        foreach (var operador in operadores)
        {
            sSql += WhereOuAnd + " p." + operador;
        }

        var parametros = operadores.Where(o => o.GetType().IsAssignableFrom(typeof(Igual))).Select(o2 => ((Igual)o2).ParametroOracle).ToList();
        var dataTable = obj.ConsultarSQl(ConexaoBancoDados, sSql, parametros);

        foreach (DataRow linha in dataTable.Rows)
        {
            yield return new Pessoa
            {
                PessoaId = Convert.ToInt32(linha["ID_PESSOA"].ToString()),
                Nome = linha["NOME_COMPLETO"].ToString(),
                Apelido = linha["APELIDO"].ToString(),
                Email = linha["EMAIL_PESSOAL"].ToString(),
                NomePai = linha["NOME_PAI"].ToString(),
                NomeMae = linha["NOME_MAE"].ToString(),
                Sexo = linha["SEXO"].ToString(),
                Cpf = linha["CPF"].ToString(),
                Rg = linha["RG"].ToString(),
                OrgaoEmissorRg = linha["ORGAO_RG"].ToString(),
                DataEmissaoRg = (!String.IsNullOrEmpty(linha["EMISSAO_RG"].ToString())) ? Convert.ToDateTime(linha["EMISSAO_RG"].ToString()) : DateTime.MinValue,
                Nascimento = (!String.IsNullOrEmpty(linha["DATA_NASC"].ToString())) ? Convert.ToDateTime(linha["DATA_NASC"].ToString()) : DateTime.MinValue,
                LocalNascimento = linha["LOCAL_NASC"].ToString(),
                Endereco = linha["ENDERECO"].ToString(),
                Numero = linha["NUMERO"].ToString(),
                Complemento = linha["COMPLEMENTO"].ToString(),
                Bairro = linha["BAIRRO"].ToString(),
                Cidade = linha["CIDADE"].ToString(),
                Estado = linha["ESTADO"].ToString(),
                DddTelefoneResidencial = (!String.IsNullOrEmpty(linha["DDD_FONE_RES"].ToString())) ? Convert.ToInt32(linha["DDD_FONE_RES"].ToString()) : Int32.MinValue,
                TelefoneResidencial = (!String.IsNullOrEmpty(linha["FONE_RES"].ToString())) ? Convert.ToInt32(linha["FONE_RES"].ToString()) : Int32.MinValue,
                DddCelular = (!String.IsNullOrEmpty(linha["DDD_FONE_CEL"].ToString())) ? Convert.ToInt32(linha["DDD_FONE_CEL"].ToString()) : Int32.MinValue,
                TelefoneCelular = (!String.IsNullOrEmpty(linha["CELULAR"].ToString())) ? Convert.ToInt32(linha["CELULAR"].ToString()) : Int32.MinValue,
                BancoTalentosId = linha["ID_BANCO_TALENTOS"].ToString(),
                EstadoCivil = linha["ESTADO_CIVIL"].ToString(),
                NacionalidadeId = (!String.IsNullOrEmpty(linha["ID_NACIONALIDADE"].ToString())) ? Convert.ToInt32(linha["ID_NACIONALIDADE"].ToString()) : Int32.MinValue,
                Deficiente = linha["DEFICIENTE"].ToString(),
                TamanhoSapato = (!String.IsNullOrEmpty(linha["TAMANHO_SAPATO"].ToString())) ? Convert.ToInt32(linha["TAMANHO_SAPATO"].ToString()) : Int32.MinValue,
                TamanhoCamiseta = linha["TAMANHO_CAMISETA"].ToString(),
                Alergia = linha["ALERGIA"].ToString(),
                NomeConjuge = linha["NOME_CONJUGE"].ToString(),
                DddTelefoneEmergencia = (!String.IsNullOrEmpty(linha["DDD_TEL_EMERGENCIA"].ToString())) ? Convert.ToInt32(linha["DDD_TEL_EMERGENCIA"].ToString()) : Int32.MinValue,
                TelefoneEmergencia = (!String.IsNullOrEmpty(linha["TEL_EMERGENCIA"].ToString())) ? Convert.ToInt32(linha["TEL_EMERGENCIA"].ToString()) : Int32.MinValue,
                DddCelularEmergencia = (!String.IsNullOrEmpty(linha["DDD_CEL_EMERGENCIA"].ToString())) ? Convert.ToInt32(linha["DDD_CEL_EMERGENCIA"].ToString()) : Int32.MinValue,
                CelularEmergencia = (!String.IsNullOrEmpty(linha["CEL_EMERGENCIA"].ToString())) ? Convert.ToInt32(linha["CEL_EMERGENCIA"].ToString()) : Int32.MinValue,
                ContatoEmergencia = linha["CONTATO_EMERGENCIA"].ToString(),
                FormacaoAcademicaId = (!String.IsNullOrEmpty(linha["ID_FORMACAO_ACADEMICA"].ToString())) ? Convert.ToInt32(linha["ID_FORMACAO_ACADEMICA"].ToString()) : Int32.MinValue,
                Parentesco = linha["PARENTESCO"].ToString(),
                FotoPessoa = (linha["FOTO_PESSOA"].ToString() != "") ? (Byte[])linha["FOTO_PESSOA"] : new byte[0],
                RI = linha["RI"].ToString(),
                TituloEleitor = linha["TITULO_ELEITOR"].ToString(),
                Zona = linha["ZONA"].ToString(),
                Secao = linha["SECAO"].ToString(),
                CarteiraTrabalho = linha["SECAO"].ToString(),
                Serie = linha["SECAO"].ToString(),
                Pis = linha["PIS"].ToString(),
                CpfConjuge = linha["CPF_CONJUGE"].ToString(),
                Linkedin = linha["LINKEDIN"].ToString(),
                Facebook = linha["FACEBOOK"].ToString(),
                Twitter = linha["TWITTER"].ToString(),
                EtniaId = (!String.IsNullOrEmpty(linha["ID_ETNIA"].ToString())) ? Convert.ToInt32(linha["ID_ETNIA"].ToString()) : Int32.MinValue,
                Cep = linha["CEP"].ToString()
            };
        }
    }
}

ConsultarSql method (I didn't write this code. It's part of the data access schema that already existed):

/// <summary>
/// Método utilizado para a execução de pesquisas no banco de dados com o envio de uma coleção de parametros.
/// </summary>
/// <param name="pStringConexao">String de conexão com o banco de dados.</param>
/// <param name="pSQL">Enviar o comando SQL que será executado.</param>
/// <param name="pParams">Coleção de parametros esperados no comeando SQL.</param>
/// <returns>Retorna um DataTable com o resultado da pesquisa.</returns>
public DataTable ConsultarSQl(string pStringConexao, string pSQL, List<OracleParameter> pParams)
{
    #region Abre a Conexão

    OracleConnection cn = new OracleConnection();

    try
    {
        cn = GetConnection(pStringConexao);
        cn.Open();
    }
    catch (Exception ex)
    {
        cn.Dispose();
        throw ex;
}

    #endregion

    OracleCommand dbCommand = new OracleCommand(pSQL, cn);
    dbCommand.CommandType = CommandType.Text;

    foreach (OracleParameter param in pParams)
    {
        if (param.Value != null)
        dbCommand.Parameters.Add(param);
        }

    OracleDataAdapter oAdp = new OracleDataAdapter(dbCommand);
    DataSet ds = new DataSet();

    try
    {
        oAdp.Fill(ds);
    }
    catch (Exception ex)
    {
        if (cn.State == ConnectionState.Open)
        {
            cn.Close();
        }

        dbCommand.Dispose();
        cn.Dispose();
        throw ex;
    }
    finally
    {
        if (cn.State == ConnectionState.Open)
        {
            cn.Close();
        }

        dbCommand.Dispose();
        cn.Dispose();
    }

    return ds.Tables[0];
}

If there is a need to put more codes, just ask via comments.

Answer:

If your method doesn't actually do any asynchronous operations, it doesn't make sense to have KeyWord async.

And asynchronous methods must be preceded by the await keyword so that they are executed and waited, freeing the current thread in the meantime.

original code

[Authorize]
public async Task<JsonResult> IndexAsync()
{
    var pessoas = new Pessoas(GeneralSettings.DataBaseConnection)
        .Selecionar()
        .Select(p => new { NOME = p.Nome, CPF = p.Cpf, FONE = p.TelefoneResidencial, CELULAR = p.TelefoneCelular, DEPARTAMENTO = "Teste", EMAIL = p.Email })
        .Take(10);

    return Json(pessoas, JsonRequestBehavior.AllowGet);
}

ideal code

Assuming that the Select method is asynchronous (which only makes sense if internally it does network or fileSystem I/O asynchronously too).

[Authorize]
public async Task<JsonResult> IndexAsync()
{
    var model = new Pessoas(GeneralSettings.DataBaseConnection);
    var pessoas = await model.Selecionar();
    var result = pessoas
        .Select(p => new { NOME = p.Nome, CPF = p.Cpf, FONE = p.TelefoneResidencial, CELULAR = p.TelefoneCelular, DEPARTAMENTO = "Teste", EMAIL = p.Email })
        .Take(10);

    return Json(result, JsonRequestBehavior.AllowGet);
}

NOTE:

Direct access to a relational database asynchronously is not recommended.

Scroll to Top