Async getting data from different views of the same database - c#

I want to fetch data from 5 different views of the same database asynchronously - I used the following solution:
public async Task<List<Product>> GetProductsAsync()
{
string query = $"SELECT * FROM dbo.v_Products";
try
{
Stopwatch sw = new Stopwatch();
sw.Start();
var items = await _dbContext.Database.SqlQuery<Product>(query).ToListAsync();
sw.Stop();
Debug.WriteLine("\t " + Thread.CurrentThread.ManagedThreadId + $" getting Products ({items.Count}) seconds: " + sw.Elapsed.TotalSeconds);
return items;
}
catch (Exception ex)
{
throw new Exception("Getting Products failed!", ex);
}
}
There is the following situation: I have ~30 databases, a thread is ran for each one and methods like "GetProductAsync" are executed for gathering data. But I haven' see improvement of using async, it seems that time of execution of each next method contains the time of execution of previous. Where could I be wrong?
UPD: Calling the function
public async Task<DataContext> GetDataAsync()
{
DataContext data = new DataContext();
var items1= await _dac.GetProductsAsync();
var items2 = await _dac.GetProducts2Async();
var items3 = await _dac.GetProducts3Async();
var items4 = await _dac.GetProducts4Async();
var items5 = await _dac.GetProducts5Async();
data.items1= items1;
data.items2= items2;
data.items3= items3;
data.items4= items4;
data.items5= items5;
return data;
}
Is it OK if I will recreate db context for each async method execution, like here?
public async Task<List<Product>> GetProductsAsync()
{
string query = $"SELECT * FROM dbo.v_Products";
var ctx = new myDbContext(_dbContext.Database.ConnectionString);
try
{
Stopwatch sw = new Stopwatch();
sw.Start();
var items = await ctx.Database.SqlQuery<Product>(query).ToListAsync();
sw.Stop();
Debug.WriteLine("\t " + Thread.CurrentThread.ManagedThreadId + $" getting Products ({items.Count}) seconds: " + sw.Elapsed.TotalSeconds);
return items;
}
catch (Exception ex)
{
throw new Exception("Getting Products failed!", ex);
}
}

Call all Async methods but Await all of them later,
public async Task<DataContext> GetDataAsync()
{
DataContext data = new DataContext();
var t1 = _dac.GetProductsAsync();
var t2 = _dac.GetProducts2Async();
var t3 = _dac.GetProducts3Async();
var t4 = _dac.GetProducts4Async();
var t5 = _dac.GetProducts5Async();
data.items1 = await t1;
data.items2 = await t2;
data.items3 = await t3;
data.items4 = await t4;
data.items5 = await t5;
return data;
}
Creating new context for each call is not going to create any problem as long as the execution time of database call is small. Each new connection uses/reuses connection from a connection pool so you should not eat up all of it.

Most probably because
Multiple active operations on the same context instance are not
supported.
Your calling method does exactly what it should do:
Use 'await' to ensure that any asynchronous operations have completed
before calling another method on this context.
https://msdn.microsoft.com/en-us/library/dn220262(v=vs.113).aspx

public async Task<DataContext> GetDataAsync()
{
DataContext data = new DataContext();
//crate individual tasks
var test1 = _dac.GetProductsAsync();
var test2 = _dac.GetProducts2Async();
var test3 = _dac.GetProducts3Async();
var test4 = _dac.GetProducts4Async();
var test5 = _dac.GetProducts5Async();
//Execute all tasks at once with WhenAll function
await Task.WhenAll(task1, task2, task3, task4, task5);
//This statement is executed only after all the tasks are finished
return data;
}
Refer MSDN Link for detailed notes on WhenAll.

Related

Asynchronous method does not return control to its caller

I have created a supposed to be asynchronous method. And inside of that it has several await statements.
I am calling this method (in another class) from the main method.
But it doesn't return the control back to the caller (main), to execute the next statement (to get more data),
even though the operation takes long time to complete.
What is wrong here?
Could it be something with the return statement? Or do I need to wrap the logic inside a Task?
internal async static Task<List<Cars>> GetCarsAsync()
{
var cars = new List<Cars>();
try
{
using (var connection = new OracleConnection(ConfigurationManager.ConnectionStrings["KWC"].ConnectionString))
{
await connection.OpenAsync(); //first await
logger.Info("Opened database connection.");
StringBuilder selectStatement = new StringBuilder(Properties.Settings.Default.SelectCarsView);
using (var command = connection.CreateCommand())
{
command.CommandText = selectStatement.ToString();
logger.Info("About to execute following query on database: {0}", command.CommandText);
using (var reader = await command.ExecuteReaderAsync()) //second await
{
logger.Info("Executed query: {0}", command.CommandText);
while (await reader.ReadAsync()) //third await
{
var car = new Car { model = reader.GetString(0) };
//more code
cars.Add(car);
}
}
}
}
return cars;
}
catch (OracleException ex)
{
logger.Fatal(ex.Message);
throw;
}
}
static async Task Main(string[] args)
{
var getCarsTask = CarsManager.GetCarsAsync();
var getSomethingElseTask = DummyManager.GetMoreDataAsync();
// more similar tasks
var cars = await getCarsTask;
var moreData = await getSomethingElseTask;
// more code
}

How to make parallel database calls and bind to model ASP.NET Core MVC

I'm using ASP.NET Core MVC 2.0 and I've got an API that calls a method which opens a DB connection and inserts values into a model.
I'm hitting the same method 7 times and I'm just passing different information into it the parameters to build out my model. I HAVE to hit it seven different times, no rewrite of code will prevent this.
Instead of creating a loop and calling that method I wish to make parallel calls to the db so the response is faster. I've found multiple articles explaining how to do this e.g. How to properly make asynchronous / parallel database calls but there's enough difference that I can't seem to get it to work.
Following is the my method hitting the DB:
public async Task<RS_Model> Get_Results(RequestS_Model values)
{
//Deleted a bunch of code to keep it simple
string strQS = #"EXEC Get param1,param2,etc";
using (SqlConnection conSQL = new SqlConnection(connectionString))
{
using (SqlCommand cmdSQL = new SqlCommand(strQS, conSQL))
{
conSQL.Open();
using (SqlDataReader dtrSQL = cmdSQL.ExecuteReader())
{
while (dtrSQL.Read())
{
Double.TryParse(dtrSQL["Value1"].ToString(), out dblVal1);
} //Ends While
} //end SQLDataReader
} //Ends cmdSQL
} //ends using
results.Price = dblVal1;
return results;
} //Ends Get Results
My IActionResult for the api is:
[HttpGet]
public async Task<IActionResult> Get([FromQuery] RequestS_Model values)
{
SV_Results Results = new SV_Results();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
RS_Model model = new RS_Model();
model.dblr[0] = await Results.Get_Results(values);
values.Parm1 = 2;
model.dblr[1] = await Results.Get_Results(values);
values.Parm1 = 3;
model.dblr[2] = await Results.Get_Results(values);
values.Parm1 = 4;
model.dblr[3] = await Results.Get_Results(values);
values.Parm1 = 5;
model.dblr[4] = await Results.Get_Results(values);
values.Parm1 = 6;
model.dblr[5] = await Results.Get_Results(values);
values.Parm1 = 7;
model.dblr[6] = await Results.Get_Results(values);
//int[] results = await Task.WhenAll(new Task<int>[] { task1, task2 });
return new OkObjectResult(model);
} //IActionResults
I know that I've forced them into a synchronous call be doing what I am, but I can't seem to make the 7 calls asynchronous and then wait for them to be all done before I build my final model. The final response needs to be in Json, but I haven't even gotten that far yet.
Instead of this:
model.dblr[0] = await Results.Get_Results(values);
model.dblr[1] = await Results.Get_Results(values);
model.dblr[2] = await Results.Get_Results(values);
model.dblr[3] = await Results.Get_Results(values);
model.dblr[4] = await Results.Get_Results(values);
model.dblr[5] = await Results.Get_Results(values);
model.dblr[6] = await Results.Get_Results(values);
Create a list of tasks and await them as a group:
var tasks = Enumerable.Range(0,7).Select( i => Results.Get_Results(values) ).ToList();
await Task.WhenAll(tasks);
for (int i=0; i<7; i++) model.dblr[i] = tasks[i].Result;

Moment of multiple tasks end while they return different objects

I've got 5 tasks like this:
private async Task<List<Places>> GetPlaces()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var list5 = await GetPlacesTask();
sw.Stop();
Testowybox3.Text = sw.Elapsed.ToString()+" get places times";
return list5;
}
private async Task<List<Categories>> GetCategories()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var list5 = await GetCategoriesTask();
sw.Stop();
Testowybox2.Text = sw.Elapsed.ToString()+" get categories times";
return list5;
}
They differs by returned value for e.g. my second task:
private async Task<EventsInstance[]> GetDetailedData()
{
Stopwatch sw = new Stopwatch();
sw.Start();
list4 = await GetDetailedDataTask();
List<EventsListPrint> lista = new List<EventsListPrint>();
foreach (var VARIABLE in list4)
{
lista.Add(new EventsListPrint() { Date = string.Format("{0:dd.MM.yyyy}", VARIABLE.startDate.Date), Time = VARIABLE.startDate.TimeOfDay.ToString(), Category = VARIABLE.categoryId.ToString(), Where = VARIABLE.organizer.designation, What = VARIABLE.name });
}
fruitdatagrid2.ItemsSource = lista;
sw.Stop();
Testowybox.Text = sw.Elapsed.ToString()+" get detailed data times";
return list4;
}
private async Task<List<Organizers>> GetOrganizers()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var list5 = await GetOrganizersTask();
sw.Stop();
Testowybox4.Text = sw.Elapsed + " get orgzanizers times";
return list5;
}
They are parsing Jsons from web. I'm collecting data from them like this:
var DetailedDataList = await GetDetailedData();
var CategoriesList = await GetCategories();
var PlacesList = await GetPlaces();
var OrganizersList = await GetOrganizers();
var EventsList = await GetEvents();
How can I assure that they're all done? I tried:
Task.WhenAll(GetDetailedData(), GetCategories(), GetPlaces(), GetOrganizers(), GetEvents());
but it seems that I cannot retrieve values then. What I really want to achieve is to create method which would be aware of successful completion of tasks and then operarate on this data in my WPF GUI. I could maybe chain them in one, but I just don't know how to do so. Everything works fine as far as I'm using one of the returned list. When trying to do linq on one list using another list, there is a chance that the another one isn't prepared yet and it simply crashes my app.
When you await Task.WhenAll, when the line resumes, you can be assured that the tasks that were supplied will be in a completed state.
var detailedDataTask = GetDetailedData();
var categoriesTask = GetCategories();
//etc
await Task.WhenAll(detailedDataTask, categoriesTask); //etc
However, the variables that contain the tasks (detailedDataTask) are still Task<T>. Not the type that they wrap, T.
How do we unwrap the completed tasks?
We can either (unintuitively, but preferably) await the completed task:
var detailedData = await detailedDataTask;
or (because we know that the tasks are complete) we can go directly for the .Result property without fear of blocking...
var detailedData = detailedDataTask.Result;
Because of the additional magic that happens when the runtime untangles your asynchronous exceptions, you should probably prefer awaiting all the way.

Run parallel validation of Uri using WebRequest

Let's say I have more Uri's. I need to validate, if they are reachable.
public RelayCommand TestConnectionCommand => new RelayCommand(async () =>
{
var res1 = await ValidateUriAsync(uri);
var res2 = await ValidateUriAsync(uri);
});
private async Task<bool> ValidateUriAsync(Uri uri)
{
try
{
var request = WebRequest.CreateHttp(uri);
var result = await request.GetResponseAsync();
return true;
}
catch (Exception e)
{
return false;
}
}
When the program comes to first await it takes some time to validate the uri, after I get the result, I can show the result on the View. Then program goes next and I validate second uri. I'd like to do that parallel, without awaiting. I was thinking about using Begin/EndGetResponse. I need to show the result for each validation on the View. Validation succeeded/failed.
Many thanks for advice.
When using await you stop the execution until the task returns, instead wait for all task to finish:
var task1 = ValidateUriAsync(uri);
var task2 = ValidateUriAsync(uri);
await Task.WhenAll(task1, task2);
or to wait until the first fault:
var tasks = new List<Task>
{
ValidateUriAsync(), ValidateUriAsync(uri)
};
while (tasks.Any())
{
var t = await Task.WhenAny(tasks);
if (t.IsFaulted)
{
//Faulty
break;
}
tasks.Remove(t);
}

How to run all tasks in array and process the results continuously (async, await)?

I have following code:
List<Task<int>> taskArray = new List<Task<int>>();
for (int i = 0; i < 100; i++)
{
taskArray.Add(myCrawler.getWebPageCharsCount("https://www.something.com"));
}
The method looks like this:
public Task<int> getWebPageCharCount(string url)
{
var client = new HttpClient();
return Task.Run(async () =>
{
Task<string> task = client.GetStringAsync(url);
string taskResult = await task;
return taskResult.Length;
});
}
After this, there is a 100 threads running, what I want to achive is to process the result in main thread after each individual task is done, not to wait to all results, which would I do with the following code:
var results = await Task.WhenAll(taskArray);
foreach (var res in results)
{
myTextBox.Text += res.ToString() + "\n";
}
I was thinking about something like this:
foreach (var task in taskArray)
{
var result = await task;
myTextBox.Text += result.ToString() + "\n";
}
But after testing and reading about await in loops, I know it's running synchronized. Is there a way to process the results continuously in main thread?
There are several solutions to this, but the simplest is to use await Task.WhenAny(), which returns a completed Task, which you then process and remove from the list of tasks, then repeat the same until the list is empty.
Example as requested:
List<Task<int>> indexingArray = new List<Task<int>>(taskArray);
var results = new int[taskArray.Count];
while (taskArray.Any())
{
Task<int> completedTask = await Task.WhenAny(taskArray).ConfigureAwait(false);
results[indexingArray.IndexOf(completedTask)] = completedTask.Result;
taskArray.Remove(completedTask);
}
myTextBox.Text = string.Join("\n", results);
(I added an indexing collection to get the correct index for the results. There are probably better ways to do that but as the question is about tasks I'll leave it as-is).
what I want to achive is to process the result in main thread after each individual task is done, not to wait to all results
The simplest solution is to introduce an async method that corresponds to that "process" concept. You already have an async way of "retrieval", you just need to introduce an async operation of "retrieve and process":
// (getWebPageCharCount is unchanged)
private Task GetAndDisplayWebPageCharCount(string url)
{
var res = await myCrawler.getWebPageCharCount(url);
myTextBox.Text += res.ToString() + "\n";
}
Used as such:
List<Task> taskArray = new List<Task>();
for (int i = 0; i < 100; i++)
taskArray.Add(GetAndDisplayWebPageCharCount("https://www.something.com"));
await Task.WhenAll(taskArray);
On a side note, there's abosolutely no need for Task.Run wrapping HttpClient.GetStringAsync. Also, the "100 threads running" remark is incorrect; there are 100 tasks in progress.

Categories

Resources