Refresh DataServiceCollection - c#

I wonder if there is a code there that refresh my DataServiceCollection that is loaded in a WPF client using BeginExecute async await Task as show below:
public static async Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
//Thread.Sleep(10000);
var queryTask = Task.Factory.FromAsync<IEnumerable<TResult>>(query.BeginExecute(null, null), (asResult) =>
{
var result = query.EndExecute(asResult).ToList();
return result;
});
return await queryTask;
}
I call the extension method as the following:
public async void LoadData()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await Task.Run(async () => await query.ExecuteAsync());
_dataSource.Clear(true);
_dataSource.Load(data);
}
LoadData is Called in a ViewModel
Change a field value by hand using SQL Management Studio
Call LoadData again <<<< Does not refresh!!!
Meanwhile, If I use Load method the data is refreshed without any problems as:
var query = _ctx.TheEntity;
_dataSource.Load(query);
Another issue is that I do not know how to Cancel client changes. Last question is whether the MergeOption has any effect to BeginExecute..EndExecute or it only works with Load method?

I suspect that the data context does not like being accessed from multiple threads.
I recommend that you first remove all processing from your extension method:
public static Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
return Task.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);
}
Which you should use without jumping onto a background thread:
public async Task LoadDataAsync()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await query.ExecuteAsync();
_dataSource.Clear(true);
_dataSource.Load(data);
}

Related

ObjectDisposedException after second async call

I'm trying to send a HTTP POST request multiple times but not in parallel. I've created another class and put this function into it:
public async Task<string> sendReqAsync()
{
requestTime = DateTime.Now;
var task = await client.PostAsync("https://api.example.com", content).ContinueWith(async (result) =>
{
responseTime = DateTime.Now;
return await result.Result.Content.ReadAsStringAsync();
});
return task.Result;
}
And finally it'll be called in an click event for a button:
private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// Here "client" is object of my class that have been initilized in the main WPF function
await client.sendReqAsync().ContinueWith((result) =>
{
var parsedResponse = JObject.Parse(result.Result);
this.Dispatcher.Invoke(() =>
{
ordersListView.Items.Add(new OrderDetailsItem
{
IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
ServerMessage = parsedResponse["MessageDesc"].ToString(),
RequestTime = client.requestTime.ToString("hh:mm:ss.fff"),
ResponseTime = client.responseTime.ToString("hh:mm:ss.fff"),
ServerLatency = (client.responseTime - client.requestTime).Milliseconds.ToString()
});
});
});
}
But it works just for the first click (event occurs) and for next click it gives me:
Inner Exception 1: AggregateException: One or more errors occurred.
Inner Exception 2: AggregateException: One or more errors occurred.
Inner Exception 3: ObjectDisposedException: Cannot access a disposed
object.
Edit: Below is the edited code as suggested, but the error is not fixed.
MyHTTPClient
public class MyHTTPClient
{
private static readonly HttpClient client = new HttpClient();
StringContent content;
public DateTime requestTime;
public DateTime responseTime;
public void configureReq(string oauthToken)
{
var postBodyJSONObject = new
{
body = "value"
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);
content = new StringContent(JsonConvert.SerializeObject(postBodyJSONObject), Encoding.UTF8, "application/json");
}
public async Task<string> sendReqAsync()
{
requestTime = DateTime.Now;
var result = await client.PostAsync("https://example.com", content);
responseTime = DateTime.Now;
return await result.Content.ReadAsStringAsync();
}
}
Also here is my window initializer function:
MyHTTPClient client; // Somewhere in the class
public MainWindow() {
client = new MyHTTPClient();
client.configureReq(AUTHORIZATION);
}
First of all: You shouldn't mix different asynchronous models of programming.
In your code you are trying to combine async/await with legacy ContinueWith approach.
You can read more about async/await approach here and about async/await and ContinueWith differences here (Thanks to MickyD for leaving link to this in the comments).
So to cut a long story short your code should look like this:
public async Task<string> SendRequestAsync()
{
requestTime = DateTime.Now;
var response = await client.PostAsync("api.example.com", content);
var responseTime = DateTime.Now;
return await response.Content.ReadAsStringAsync();
}
Note that now code looks much more like synchronous and is a way readable than callbacks with ContinueWith.
You can find more about asynchronous programming in C# on MSDN. I suggest you to read and try to exectue some code in console app before writing it in yourmain app.
In addition to fixing the SendRequestAsync method, as shown by Neistow's answer, you must fix the button's click handler too:
private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
{
string response = await client.SendRequestAsync();
var parsedResponse = JObject.Parse(response);
ordersListView.Items.Add(new OrderDetailsItem
{
IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
ServerMessage = parsedResponse["MessageDesc"].ToString(),
RequestTime = client.RequestTime.ToString("hh:mm:ss.fff"),
ResponseTime = client.ResponseTime.ToString("hh:mm:ss.fff"),
ServerLatency = (client.ResponseTime - client.RequestTime).Milliseconds.ToString()
});
}
There is no reason to mess with the Dispatcher.Invoke. The code after the await will continue running on the UI thread, as it happens by default in all Windows Forms and WPF applications.

Calling Async method

I am trying calling the async method SendParkInfo method using the await operator like this
await Task.WhenAny(parkList);and await Task.WhenAny(parkInfo);
parkInfo has SendParkInfo method object
Here is some part of my code.
public async Task<AvailableParksResponse> GetAvailableParks(IEnumerable<string> parkRegionList)
{
//
var parkList = parkRegionList.Select(x => SendParkInfo(x)).ToList();
var parkListTask = await Task.WhenAny(parkList);
response.ParkInfoList = new List<Task<ParkInfo>> { parkListTask };
var parkInfo = SendParkInfo(id);
var parkTask = await Task.WhenAny(parkInfo);
response.ParkInfo = new List<Task<ParkInfo>> { parkTask };
//
}
public virtual async Task<ParkInfo> SendParkInfo(string id)
{
//
var apiResponse = await apiClient.GetAsync(RequestUri + id);
//
}
Is it ok to call SendParkInfo like the way I am calling and then using await operator with Task.WhenAny method. Or is there any better way of calling the Async SendParkInfo method
Any help or suggestion would be appreciated.
Thanks in advance
Task.WhenAny() will return a Task which is considered completed when at least one item in the list of tasks passed into WhenAny() has completed. This usually an appropriate option if you want to return data incrementally, as processing completes.
Alternatively, if your intent is to only return a result when all async tasks have completed, consider Task.WhenAll().

Why doesn't my function await the end of ForEachAsync?

I'm trying to fill my model with data that I get from an asynchronous operation to my database. The problem is that the function returns the View (without the completed model), despite my await call.
I have tried to put a timer (I know that is not the solution), to be sure that the problem come from the asynchronous, I have also tried to put on comment some part of code inside my ForEachAsync, but it doesn't seem to help.
I get a list of project, that I fill with some additional information, finally, I assign my object to my model then return the View
public async Task<IActionResult> newProjetList(int GestionaireId, int VilleId)
{
ProjetListModel model = new ProjetListModel();
ProjetService projetService = new ProjetService(connectionString);
UserServices userServices = new UserServices(connectionString);
AvancementService avancementService = new AvancementService(connectionString);
VilleService villeService = new VilleService(connectionString);
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
await projets.ToAsyncEnumerable().ForEachAsync(async p =>
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
await p.SousProjet.ToAsyncEnumerable().ForEachAsync(async sp =>
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
});
});
model.projets = projets;
//System.Threading.Thread.Sleep(5000);
return View("ProjetList", model);
}
I expected an output with the missing information (here are the 'ville', 'gestionnairesProjet' and 'Avancement'
ForEachAsync only takes an Action<...>, not a Func<..., Task>, so the async lambda your code is passing to ForEachAsync is becoming an async void method. One of the primary reasons async void should be avoided is that it's not easy to determine when the method completes - and in fact, in this case, there is nothing ensuring that it will complete before sending the response.
I recommend doing what Marc suggested and just using foreach:
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
foreach (var p in projects)
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
foreach (var sp in p.SousProject)
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
}
}
model.projets = projets;
Or, if you want to use asynchronous concurrency, you can make use of Task.WhenAll:
List<Projet> projets = await projetService.NewProjetLine(GestionaireId, VilleId);
await Task.WhenAll(projects.Select(async p =>
{
int villeId = await villeService.getVilleIdByProjetId(p.ProjetId);
Ville ville = await villeService.GetById(villeId);
p.Ville = ville.VilleLabel;
p.GestionnaireProjet = await userServices.GetNameById(p.GestionnaireProjetId ?? 0);
await Task.WhenAll(p.SousProject.Select(async sp =>
{
sp.Avancement = await avancementService.GetLabelById(sp.AvancementId);
});
});
model.projets = projets;
Use ForEachAwaitAsync instead of ForEachAsync
Explanation: ForEachAsync can't wait since it is simply a multi-threaded execution of your loop (accepts Action). In fact you might have received a compiler warning by using async for your lambda return, because you intend to assign a Task to a void (unused)
ForEachAwaitAsync will wait because it accepts a Func<Task> and internally also awaits / asyncs accordingly.
For curious minds, you can see the source code here: https://github.com/dotnet/reactive/blob/main/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ForEach.cs mentioned as ForEachAwaitAsyncCore and ForEachAsync

Async Task not returning to main thread

I think this has something to do with my really bad async programming.
Here it goes. I am fetching mailchimp subscribers using an async method but the result just hangs and never returns to main thread.
The async method
public async Task<List<Subscriber>> GetMailChimpSubscribers()
{
TaskCompletionSource<List<Subscriber>> result = new TaskCompletionSource<List<Subscriber>>();
await Task.Run(async () =>
{
var subscribers = new List<Subscriber>();
var listId = "";
var members = await _manager.Members.GetAllAsync(listId);
foreach (var item in members)
{
var sub = new Subscriber();
sub.Email = item.EmailAddress;
subscribers.Add(sub);
}
result.SetResult(subscribers);
});
return result.Task.Result;
}
This hangs after result.SetResult(subscribers) statement completely.
This is called from
public static List<Subscriber> GetSubscribers()
{
MailchimpHelper helper = new MailchimpHelper();
var subscribers= helper.GetMailChimpSubscribers();
return subscribers.Result;
}
What exactly is wrong here? Is the setup wrong?
PS : there isn't an issue with mailchimp or the api , it works great in console. this is purely something bad with async programming
UPDATE:
In case this is faced by someone. the blog helped to clear a lot
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
the working solution from below answers and blog.
public async Task<List<Subscriber>> GetMailChimpSubscribers()
{
var subscribers = new List<Subscriber>();
var listId = "";
var members =
await _manager.Members.GetAllAsync(listId).**ConfigureAwait(false)**;
foreach (var item in members)
{
var sub = new Subscriber();
sub.Email = item.EmailAddress;
subscribers.Add(sub);
}
return subscribers;
}
public static List<Subscriber> GetSubscribers()
{
MailchimpHelper helper = new MailchimpHelper();
var subscribers= helper.GetMailChimpSubscribers();
subscribers.Wait();
return subscribers.Result;
}
There's a lot wrong here:
The actual problem is blocking on asynchronous code, which causes a deadlock.
There's also a confusing and unnecessary use of TaskCompletionSource<T>.
It also appears that the Task.Run is unnecessary.
Removing all the bad parts leaves us with:
public async Task<List<Subscriber>> GetMailChimpSubscribersAsync()
{
var subscribers = new List<Subscriber>();
var listId = "";
var members = await _manager.Members.GetAllAsync(listId);
foreach (var item in members)
{
var sub = new Subscriber();
sub.Email = item.EmailAddress;
subscribers.Add(sub);
}
return subscribers;
}
public static async Task<List<Subscriber>> GetSubscribersAsync()
{
MailchimpHelper helper = new MailchimpHelper();
return await helper.GetMailChimpSubscribersAsync();
}
Don't use .Result in environments with a synchronization context - it creates a circular wait.
In general, try to use async "all the way down" if possible - i.e. if you're going to use async/await at all, you should only use async/await.
Please see Don't Block on Async Code by Stephen Cleary for more details.
The first part of the problem is that you are, for some weird reason, wrapping the task inside another task. Change your method to this:
public async Task<List<Subscriber>> GetMailChimpSubscribers()
{
var subscribers = new List<Subscriber>();
var listId = "";
var members = await _manager.Members.GetAllAsync(listId);
foreach (var item in members) //this foreach could be a simpler LinQ statement
{
var sub = new Subscriber();
sub.Email = item.EmailAddress;
subscribers.Add(sub);
}
return subscribers;
}
You should also be calling this method using await, but if this is not possible then change your method to this:
public static List<Subscriber> GetSubscribers()
{
MailchimpHelper helper = new MailchimpHelper();
var subscribers = helper.GetMailChimpSubscribers();
subscribers.Wait();
return subscribers.Result;
}

Why can't I debug code in an async method?

I actually started the night trying to learn more about MongoDB, but am getting hung up and the .NET await/async stuff. I am trying to implement the code shown on MongoDB's site. I've had to modify it a tad bit, so I could get my program to compile. I now have the following in my console application:
protected static IMongoClient _client;
protected static IMongoDatabase _database;
static void Main(string[] args)
{
_client = new MongoClient();
_database = _client.GetDatabase("test");
GetDataAsync();
}
private static async void GetDataAsync() //method added by me.
{
int x = await GetData();
}
private static async Task<int> GetData()
{
var collection = _database.GetCollection<BsonDocument>("restaurants");
var filter = new BsonDocument();
var count = 0;
Func<int> task = () => count; //added by me.
var result = new Task<int>(task); //added by me.
using (var cursor = await collection.FindAsync(filter)) //Debugger immediately exits here, goes back to main() and then terminates.
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
foreach (var document in batch)
{
// process document
count++;
}
}
}
return count; //added by me
}
When I run the application, the debugger will call into my GetDataAsync() method which in turn calls into the GetData() method. It gets to the line using (var cursor = await collection.FindAsync(filter)) and then immediately returns to finish the main() method.
Any break points I put below that line are ignored, as are any breakpoints I put in the GetDataAsync() method. Is this code just not getting run because the program exits? Can someone explain to me what is going on?
Because you are not awaiting your GetDataAsync method. When the first await is reached the thread is returned to the caller. Since you are not waiting for the completion of the task, your console application exits and your breakpoint is not reached. You will also need to update the GetDataAsync method to return a Task rather than void. You cannot await void. You should avoid using async void for anything other than the event handler.
protected static IMongoClient _client;
protected static IMongoDatabase _database;
static void Main(string[] args)
{
_client = new MongoClient();
_database = _client.GetDatabase("test");
GetDataAsync().Wait();
// Will block the calling thread but you don't have any other solution in a console application
}
private static async Task GetDataAsync() //method added by me.
{
int x = await GetData();
}
private static async Task<int> GetData()
{
var collection = _database.GetCollection<BsonDocument>("restaurants");
var filter = new BsonDocument();
var count = 0;
Func<int> task = () => count; //added by me.
var result = new Task<int>(task); //added by me.
using (var cursor = await collection.FindAsync(filter)) //Debugger immediately exits here, goes back to main() and then terminates.
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
foreach (var document in batch)
{
// process document
count++;
}
}
}
return count; //added by me
}
I' not so good with async dev and had a similar problem, however I was kicking off the async method in Main like:
Task.Run(async () => await GetDataAsync());
I think the Garbage Collector was disposing of the anonymous method as nothing had a reference to it anymore. Using Fabien's .Wait() allowed me to step through the program and debug. I am using netcore 2.1 with vs2017

Categories

Resources