Warm tip: This article is reproduced from stackoverflow.com, please click
ado.net asp.net-core-3.1 async-await c# task

Net Core 3.1 Return a Task with a result that includes details of the exception?

发布于 2020-04-15 10:43:03

I am using ADO.Net to fill a datatable, the datatable fill method I have put into a separate repository. I inject the repository/interface into a test page model. The page model method "OnPostUpdateAlarmDataTableAsync"needs to call the interface and request the datatable to to be filled. I want to return the result from the task that fills this table, so if the task fails for what ever reasons, I can then inform the user on the UI, I also want to log the details of the error from the page model since this is the calling thread.

Current compile error says "cannot assign void to an implicitly-typed variable" But what ever combination in code changes I try, something never compiles properly. Being able to return the task from an async operation with the relevant info that contains details about the exception is what I want to achieve.

Interface:

public interface IAdoNetRepository
{
    Task FillAlarmsDataTable();
}

Repository:

public class AdoNetRepository : IAdoNetRepository
{
    public readonly IConfiguration _config;

    public AdoNetRepository(IConfiguration config)
    {
        _config = config;
    }

    // Used for method below GetUserGroupsData()
    public static SqlDataAdapter dataAdapter = new SqlDataAdapter();
    public static DataTable alarmDataTable;


    public async Task FillAlarmsDataTable()
    {
        string connectionString = _config.GetConnectionString("Data:DefaultConnection:ConnectionString");

        try
        {
            SqlConnection connection = new SqlConnection(connectionString);
            string cmdText1 = "SELECT * FROM [dbo].[Alarms] ORDER BY Name";

            // Create a new data adapter based on the specified query.
            dataAdapter = new SqlDataAdapter(cmdText1, connection);

            // Populate a new data table and bind it to the BindingSource.
            alarmDataTable = new DataTable
            {
                Locale = CultureInfo.InvariantCulture
            };
            await Task.Run(() => dataAdapter.Fill(alarmDataTable));
            //dataAdapter.Fill(alarmDataTable);

            return; // Return what ?
        }
        catch (Exception ex)
        {
            // Return the task with details of the exception
            return; // Return what?
        }
    }
}

PageModel:

public class TestModel : PageModel
{

    private readonly IAdoNetRepository _adoNetRepository;

    public TestModel(IAdoNetRepository adoNetRepository)
    {
        _adoNetRepository = adoNetRepository;
    }

    public async Task<IActionResult> OnPostUpdateAlarmDataTableAsync()
    {
        // This gets squiggly line compile error            
        var result = await _adoNetRepository.FillAlarmsDataTable();

        if (!result.Succeeded)
        {
            // log the exception returned from the task that failed!
        }

        return new JsonResult(result);
    }
}
Questioner
OJB1
Viewed
79
Nkosi 2020-02-04 10:36

The comments in the original example shows what you should actually be doing.

you would need to create a model to store the desired information.

Something like

/// <summary>
/// Represents the result of an ADO.Net operation.
/// </summary>
public class AdoNetResult {
    private List<Exception> _errors = new List<Exception>();

    public bool Succeeded { get; protected set; }
    public IEnumerable<Exception> Errors => _errors;

    public static AdoNetResult Success { get; } = new AdoNetResult { Succeeded = true };
    public static AdoNetResult Failed(params Exception[] errors) {
        var result = new AdoNetResult { Succeeded = false };
        if (errors != null) {
            result._errors.AddRange(errors);
        }
        return result;
    }
}

The interface however would need to be refactored

public interface IAdoNetRepository {
    Task<AdoNetResult> FillAlarmsDataTable();
}

and implementation

public async Task<AdoNetResult> FillAlarmsDataTable() {
    string connectionString = _config.GetConnectionString("Data:DefaultConnection:ConnectionString");

    try {
        SqlConnection connection = new SqlConnection(connectionString);
        string cmdText1 = "SELECT * FROM [dbo].[Alarms] ORDER BY Name";

        // Create a new data adapter based on the specified query.
        dataAdapter = new SqlDataAdapter(cmdText1, connection);

        // Populate a new data table and bind it to the BindingSource.
        alarmDataTable = new DataTable {
            Locale = CultureInfo.InvariantCulture
        };
        await Task.Run(() => dataAdapter.Fill(alarmDataTable));

        // Return what ?
        return AdoNetResult.Success; 
    } catch (Exception ex) {
        // Return the task with details of the exception
        return AdoNetResult.Failed(ex);
    }
}

With that done the original code should work as intended

public async Task<IActionResult> OnPostUpdateAlarmDataTableAsync() {

    var result = await _adoNetRepository.FillAlarmsDataTable();

    if (!result.Succeeded) {
        // log the exception returned from the task that failed!
        //result.Errors
    }

    return new JsonResult(result);
}

An alternative would be to let FillAlarmsDataTable throw any exceptions that it may encounter

public Task FillAlarmsDataTable() {
    string connectionString = _config.GetConnectionString("Data:DefaultConnection:ConnectionString");

    SqlConnection connection = new SqlConnection(connectionString);
    string cmdText1 = "SELECT * FROM [dbo].[Alarms] ORDER BY Name";

    // Create a new data adapter based on the specified query.
    dataAdapter = new SqlDataAdapter(cmdText1, connection);

    // Populate a new data table and bind it to the BindingSource.
    alarmDataTable = new DataTable {
        Locale = CultureInfo.InvariantCulture
    };
    return Task.Run(() => dataAdapter.Fill(alarmDataTable));
}

capture (catch) the exception where it is being used and respond accordingly

public async Task<IActionResult> OnPostUpdateAlarmDataTableAsync() {
    try {
        await _adoNetRepository.FillAlarmsDataTable();
        return Ok(); //200 OK
    catch(Exception ex) {
        //...log the exception returned from the task that failed!

        return new ExceptionResult(ex, includeErrorDetail:true);
    }
}