Evan Evan - 3 months ago 7
C# Question

Calling Asycn methods from ActionResults

I have an

ActionResult
method that uploads a file and calls the
CSVReader();
method which reads the file and writes it to a database. However, when the code runs I get an error regarding the
ActionResult
method not being async. Here is my code: (There is a lot)

public class files
{
public ObjectId _id { get; set; }
public string product_name { get; set; }
public string part_number { get; set; }
public string quantity { get; set; }
public string customer { get; set; }
public string reference { get; set; }
public string contact { get; set; }
public string quote_number { get; set; }
public string customer_po { get; set; }
public string order_date { get; set; }
//public string count { get; set; }
}


public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}


[HttpPost]
public ActionResult Index(HttpPostedFileBase file)

{
if (file != null && file.ContentLength > 0)
{
var fileName = System.IO.Path.GetFileName(file.FileName);
var path = System.IO.Path.Combine(("C:\\Dev\\ProductionOrderWebApp\\Uploads"), fileName);
file.SaveAs(path);
CSVReader(fileName);
}
return RedirectToAction("Index");
}

public static async void CSVReader(string fileName)
{

StreamReader oStreamReader = new StreamReader(fileName);

DataTable oDataTable = null;
int RowCount = 0;
string[] ColumnNames = null;
string[] oStreamDataValues = null;
//using while loop read the stream data till end
while (!oStreamReader.EndOfStream)
{
String oStreamRowData = oStreamReader.ReadLine().Trim();
if (oStreamRowData.Length > 0)
{
oStreamDataValues = oStreamRowData.Split(',');
//Bcoz the first row contains column names, we will poluate
//the column name by
//reading the first row and RowCount-0 will be true only once
if (RowCount == 0)
{
RowCount = 1;
ColumnNames = oStreamRowData.Split(',');
oDataTable = new DataTable();

//using foreach looping through all the column names
foreach (string csvcolumn in ColumnNames)
{
DataColumn oDataColumn = new DataColumn(csvcolumn.ToUpper(), typeof(string));

//setting the default value of empty.string to newly created column
oDataColumn.DefaultValue = string.Empty;

//adding the newly created column to the table
oDataTable.Columns.Add(oDataColumn);
}
}
else
{
//creates a new DataRow with the same schema as of the oDataTable
DataRow oDataRow = oDataTable.NewRow();

//using foreach looping through all the column names
for (int i = 0; i < ColumnNames.Length; i++)
{
oDataRow[ColumnNames[i]] = oStreamDataValues[i] == null ? string.Empty : oStreamDataValues[i].ToString();
}

//adding the newly created row with data to the oDataTable
oDataTable.Rows.Add(oDataRow);
}
}
}
//close the oStreamReader object
oStreamReader.Close();
//release all the resources used by the oStreamReader object
oStreamReader.Dispose();

//Looping through all the rows in the Datatable
foreach (System.Data.DataRow oDataRow in oDataTable.Rows)

{
Upload vtx = new Upload();
files csv = new files();
csv.product_name = oDataRow[0].ToString();
csv.part_number = oDataRow[1].ToString();
csv.quantity = oDataRow[2].ToString();
csv.customer = oDataRow[3].ToString();
csv.reference = oDataRow[4].ToString();
csv.contact = oDataRow[5].ToString();
csv.quote_number = oDataRow[6].ToString();
csv.customer_po = oDataRow[7].ToString();
csv.order_date = oDataRow[8].ToString();
//csv.count = Convert.ToString(count);

await vtx.VIGL.InsertOneAsync(csv);
//count++;

string RowValues = string.Empty;

//Looping through all the columns in a row
//var count = 1;
foreach (string csvcolumn in ColumnNames)

{

}
}
}

public class Upload //Constructor
{
public IMongoDatabase db;
internal Upload()
{

MongoClient client = new MongoClient();//insert connection string in brackets
this.db = client.GetDatabase("POWA");
var collection = db.GetCollection<files>("Imported");
}

internal IMongoCollection<files> VIGL // Good idea to expose all collections as properties
{
get
{


return db.GetCollection<files>("Imported");
}
}

}



public ActionResult FileDisplay()
{
return View();
}


}


I've gotten it to work outside of MVC (Console) but the
ActionResult
throws an async error. How do I call the
CSVReader();
method without throwing the error?

Thanks

Answer

It is best for you to use async/await down the whole call stack which is possible with MVC. Mark your Action as async and then use the await keyword to wait for the results from the CsvReader method.

// changed signature to async and Task<ActionResult>
[HttpPost]
public async Task<ActionResult> Index(HttpPostedFileBase file)
{
    if (file != null && file.ContentLength > 0)
    {
        var fileName = System.IO.Path.GetFileName(file.FileName);
        var path = System.IO.Path.Combine(("C:\\Dev\\ProductionOrderWebApp\\Uploads"), fileName);
        file.SaveAs(path);
        // add await
        await CSVReader(fileName);
    }
    return RedirectToAction("Index");
}

// did not check the code in the method but the signature should return type Task
public static async Task CSVReader(string fileName) {/*existing/unchanged code*/}

Side note

Really the best thing for you to do is to do a little reading on the async/await pattern so you are not just copy/pasting without understanding what is actually going on and why you are using it. It will also help you troubleshoot/debug possible issues later on.