i'm currently coding C# app for Windows Store.
I have Cache class, News UserControl class and MainPage class
I'm calling in MainPage constructor Cache class and then call InitializeData for News class where i using data from Cache, but there is problem, in Cache constructor i receiving datas but he didnt do whole function, he switching from Cache constructor to InitializeData at third await function.
MainPage:
public MainPage()
{
this.InitializeComponent();
Cache.Cache cache = new Cache.Cache();
NewsContent.InitializeData(cache.MyData);
}
Cache:
public Cache()
{
Initialization = Init();
}
public Task Initialization
{
get;
private set;
}
private async Task Init()
{
try
{
cS = await folder.CreateFileAsync("cache.txt", CreationCollisionOption.OpenIfExists);
cS_titles = await folder.CreateFileAsync("titles_cache.txt", CreationCollisionOption.OpenIfExists);
string contentOfFile = await FileIO.ReadTextAsync(cS);
int contentLength = contentOfFile.Length;
if (contentLength == 0) // download data for first using
{
await debug.Write("Is empty!");
//.......
// ....
await FileIO.AppendTextAsync(cS, file_content);
await FileIO.AppendTextAsync(cS_titles, file_content_titles);
}
else // check for same data, if isnt same download new, else nothing
{
await debug.Write(String.Format("Isnt empty. Is long: {0}", contentLength)); // here he break and continue to NewsContent.InitializeData(cache.MyData);
// ....
// ....
}
await MyFunction(); // i need get constructor to this point then he will do NewsContent.InitializeData(cache.MyData);
}
catch (Exception)
{
}
}
Is this possible to do it? For any idea thank you!
Stephen Cleary's article on async and constructors describes how to make this work.
In your case, I think the factory pattern (as suggested in Jon's answer) won't work for MainPage, because it's a GUI component. But the second approach, The Asynchronous Initialization Pattern, will work.
You already implemented that pattern for Cache, now you also need to implement it for MainPage:
public MainPage()
{
Initialization = InitializeAsync();
}
public Task Initialization { get; private set; }
private async Task InitializeAsync()
{
Cache.Cache cache = new Cache.Cache();
await cache.Initialization;
NewsContent.InitializeData(cache.MyData);
}
If MainPage has some events that depend on the initialization being complete, you can make then async and add await this.Initialization at their beginning. Also, you might want to enable buttons or things like that at the end of MainPage's InitializeAsync().
This is what happens when you call an async method and never wait for it to finish, basically.
The whole point of an async method is that you don't block... and your constructor can't be asynchronous itself.
One option would be to write an asynchronous static method to create a cache:
static async Task<Cache> CreateCache()
{
// Change your InitializeData to return the data which the cache needs
var data = await InitializeData();
return new Cache(data);
}
Fundamentally you still need whatever calls CreateCache to understand that it's happening asynchronously though. You don't want to block the UI thread waiting for it all to initialize.
EDIT: I hadn't spotted that this is called from the MainPage constructor. You could potentially apply the same approach again:
public static async Task<MainPage> CreateMainPage()
{
var cache = await Cache.CreateCache();
return new MainPage(cache);
}
This is assuming you really, really can't let the main page be created without the cache being completely initialized. If you could handle that (e.g. showing something like a "Loading..." status until it's finished initializing) then that would be better.
Related
I am working on an ASP.NET Core Blazor application with .Net Core 3.0 (I am aware of 3.1, but due to Mordac I am stuck with this version for now).
I have a multiple-component page, and some of those components require access to the same data and need to all be updated when the collection is updated. I've been trying to use EventHandler-based callbacks, but those get invoked on their own threads at about the same time (if I understand correctly), causing the callbacks in the .razor components to attempt to make service calls to the context at the same time.
Note: I've tried making my DbContext`s lifetime transient, but I'm still getting the race conditions.
It's quite possible that I gotten myself into an async blender and don't know how to get out.
I've tentatively concluded that the event EventHandler methodology will not work here. I need some way to trigger "collection changed" updates to the components without triggering a race condition.
I've thought about updating the services involved in these race conditions with the following:
Replace every search function with a publically bindable collection property
Having every create/update/delete call update every single one of these collections
This would allow the components to bind directly to the collections that are changed, which I think will cause every binding to it in any component to update without the needing to be explicitly told, and this in turn would allow me to ditch the "collection changed" event handling entirely.
But I'm hesitant to try this and haven't done it yet because it would introduce a fair amount of overhead on each major service function.
Other ideas? Please help. If a collection has changed, I want Blazor components that rely on that collection to somehow be able to update, whether through notifications or binding or some other way.
The following code is a heavy simplification of what I've got, and it's still causing race conditions when the event handlers are invoked from the service.
Model
public class Model
{
public int Id { get; set; }
public string Msg { get; set; }
}
MyContext
public class MyContext : DbContext
{
public MyContext() : base()
{
Models = Set<Model>();
}
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
Models = Set<Model>();
}
public DbSet<Model> Models { get; set; }
}
ModelService
public class ModelService
{
private readonly MyContext context;
private event EventHandler? CollectionChangedCallbacks;
public ModelService(MyContext context)
{
this.context = context;
}
public void RegisterCollectionChangedCallback(EventHandler callback)
{
CollectionChangedCallbacks += callback;
}
public void UnregisterCollectionChangedCallback(EventHandler callback)
{
CollectionChangedCallbacks -= callback;
}
public async Task<Model[]> FindAllAsync()
{
return await Task.FromResult(context.Models.ToArray());
}
public async Task CreateAsync(Model model)
{
context.Models.Add(model);
await context.SaveChangesAsync();
// No args necessary; the callbacks know what to do.
CollectionChangedCallbacks?.Invoke(this, EventArgs.Empty);
}
}
Startup.cs (excerpt)
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
string connString = Configuration["ConnectionStrings:DefaultConnection"];
services.AddDbContext<MyContext>(optionsBuilder => optionsBuilder.UseSqlServer(connString), ServiceLifetime.Transient);
services.AddScoped<ModelService>();
}
ParentPage.razor
#page "/simpleForm"
#using Data
#inject ModelService modelService
#implements IDisposable
#if (AllModels is null)
{
<p>Loading...</p>
}
else
{
#foreach (var model in AllModels)
{
<label>#model.Msg</label>
}
<label>Other view</label>
<ChildComponent></ChildComponent>
<button #onclick="(async () => await modelService.CreateAsync(new Model()))">Add</button>
}
#code {
private Model[] AllModels { get; set; } = null!;
public bool ShowForm { get; set; } = true;
private object disposeLock = new object();
private bool disposed = false;
public void Dispose()
{
lock (disposeLock)
{
disposed = true;
modelService.UnregisterCollectionChangedCallback(CollectionChangedCallback);
}
}
protected override async Task OnInitializedAsync()
{
AllModels = await modelService.FindAllAsync();
modelService.RegisterCollectionChangedCallback(CollectionChangedCallback);
}
private void CollectionChangedCallback(object? sender, EventArgs args)
{
// Feels dirty that I can't await this without changing the function signature. Adding async
// will make it unable to be registered as a callback.
InvokeAsync(async () =>
{
AllModels = await modelService.FindAllAsync();
// Protect against event-handler-invocation race conditions with disposing.
lock (disposeLock)
{
if (!disposed)
{
StateHasChanged();
}
}
});
}
}
ChildComponent.razor
Copy-paste (for the sake of demonstration) of ParentPage minus the label, ChildComponent, and model-adding button.
Note: I've also experimented with attempting to insert a block of code into the HTML portion of the component, but that didn't work either since I can't use an await there.
Possibly bad idea that I experimented with (and that still didn't avoid the threading collision):
#if (AllModels is null)
{
<p><em>Loading...</em></p>
#Load();
#*
Won't compile.
#((async () => await Load())());
*#
}
else
{
...every else
}
#code {
...Initialization, callbacks, etc.
// Note: Have to return _something_ or else the #Load() call won't compile.
private async Task<string> Load()
{
ActiveChargeCodes = await chargeCodeService.FindActiveAsync();
}
}
Please help. I'm experimenting in (for me) uncharted territory.
Since i'm currently in a situation that looks awfully lot like yours, let me share what i found out. My issue was "StateHasChanged()". Since i've seen that call in your code too, maybe the following helps:
i got a pretty simple callback handler:
case AEDCallbackType.Edit:
// show a notification in the UI
await ShowNotification(new NotificationMessage() { Severity = NotificationSeverity.Success, Summary = "Data Saved", Detail = "", Duration = 3000 });
// reload entity in local context to update UI
await dataService.ReloadCheckAfterEdit(_currentEntity.Id);
the notification function does this:
async Task ShowNotification(NotificationMessage message)
{
notificationService.Notify(message);
await InvokeAsync(() => { StateHasChanged(); });
}
the reload function does this:
public async Task ReloadCheckAfterEdit(int id)
{
Check entity = context.Checks.Find(id);
await context.Entry(entity).ReloadAsync();
}
The problem was the StateHasChanged() call. It tells the UI to re-render. The UI consists of a datagrid component. The datagrid calls a query in the dataservice, to fetch data from the DB.
This happens just right before "ReloadAsync" is called, which is "awaited". Once ReloadAsync actually executes, it happens in a different thread, causing the dreaded "A second operation started on this context before a previous operation completed" exception.
My Solution was to remove the StateHasChanged line completely from where it was, and call it once after everything else was completed. No more concurrent caller issues.
Good luck solving this, i feel your pain.
I have a simple Winforms application. I would like to background TCP connections/print requests and check the output of all tasks at a set point in my code.
I would expect ReportOnTasks to block until WaitAll is complete. Please could someone explain why this is not the case? I'm also worried I haven't structured this correctly.
Edit, to clarify my intentions:
I would like to send the print jobs as soon as I receive the data. Then continue with some other DB operations. Once all the print operations are complete, I would like to update the UI to state the result.
I've attempted to simplify the code as much as I can. Maybe too much. HomeController just inits some stuff. There are buttons on the form and file watchers that trigger the main functionality.
public class HomeController
{
public HomeController(){
MessageBox.Show("1");
oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));
MessageBox.Show("2");
// Block here untill tasks are complete
ReportOnTasks();
MessageBox.Show("Report on tasks complete");
}
public async void ReportOnTasks()
{
await Task.WhenAll(oPrintController.Tasks);
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
}
and the PrintController
public class PrintController
{
public List<Task<PrintResult>> Tasks = new List<Task<PrintResult>>();
public async void PrintAsync(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
{
var s = await Task.Run(() => PrintAsync1(sIP, lsToPrint));
}
public async System.Threading.Tasks.Task<PrintResult> PrintAsync1(string sIP, List<byte[]> lsToPrint, int iPort = 9100)
{
using (TcpClient tc = new TcpClient())
{
await tc.ConnectAsync(sIP, iPort);
using (var ns = tc.GetStream())
{
foreach (byte[] btLabel in lsToPrint)
{
await ns.WriteAsync(btLabel, 0, btLabel.Length);
}
}
}
Thread.Sleep(10000);
return new PrintResult();
}
}
public class PrintResult
{
bool bSuccess = false;
}
You are not awaiting the call to ReportOnTasks()
Moreover, you can't await within a ctor, because they can't be async.
Depending on how your HomeController is used, you could use a static async method which returns an instance of HomeController, created by a private ctor instead:
Something like this:
public class HomeController
{
//notice private - you can't new up a HomeController - you have to use `CreateInstance`
private HomeController(){
MessageBox.Show("1");
//not clear from your code where oPrintController comes from??
oPrintController.PrintAsync("192.168.2.213", Encoding.ASCII.GetBytes("string to print"));
MessageBox.Show("2");
MessageBox.Show("Report on tasks complete");
}
public static async Task<HomeController> CreateInstance() {
var homeController = new HomeController();
await homeController.ReportOnTasks();
return homeController;
}
//don't use async void! Change to Task
public async Task ReportOnTasks()
{
//not clear from your code where oPrintController comes from??
await Task.WhenAll(oPrintController.Tasks);
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
}
Usage:
var homeControllerInstance = await HomeController.CreateInstance();
It's generally not recommended to perform heavy operations in class constructors, but I suppose you won't change that part, so in order to wait for ReportOnTasks to finish, you need to make it synchronous.
Take into account, that constructor itself doesn't support async/await, it's not possible to mark it async.
Having said that, you won't have real performance enhancement marking void ReportOnTasks as async. In addition, it is not recommended to mark void methods as async due to issues with exceptions handling, which is usually not possible.
So, you can either postpone ReportOnTasks like Alex showed you, or you can synchronously wait until all tasks are finished (which is possible inside ctor).
public void ReportOnTasks()
{
Task.WhenAll(oPrintController.Tasks).GetAwaiter().GetResult(); //synchronously wait
foreach(Task<PrintController.PrintResult> PR in oPrintController.Tasks)
{
// do something with the result of task
}
}
However, I wouldn't suggest this approach, because instance creation will take a while and most importantly block UI thread - and that's usually signal something is really fishy
I'm not sure about this state. I need to get data from database asynchrony.
I have class DB
public class Db{
public async Task<ObservableCollection<Person>> GetAllPerson()
{
using (var context = new Db())
{
// get data and return ObservableCollection<Person>
}
}
}
In the ViewModel I call LoadData function.
public class VM{
public ObservableCollection<Person> Person { get; set; }
private readonly DB sqlRepository;
public VM()
{
sqlRepository=new DB();
LoadData();
}
private async void LoadData()
{
Person= await sqlRepository.GetAllPerson();
}
}
I got warning: Warning CS1998 This async method lacks 'await' operators and will run synchronously.
How can I run my function asynchronously?
Should I use ?
Person=await Task.Run(()=>this.sqlRepository.GetAllPerson());
How can I run my function asynchronously?
You're approaching your problem from the wrong direction. You're trying to go "outside in" - your ViewModel wants to load the database data asynchronously. And that's a fine way of describing the problem, but it's the wrong way to solve it.
To solve it more easily, start at the other end. Whatever methods are actually calling into the database (e.g., Entity Framework calls) should be made asynchronous first, and then let async grow out from there. Eventually you'll end up with something like:
public async Task<ObservableCollection<Person>> GetAllPersonAsync()
{
using (var context = new Db())
{
// This code wasn't shown in the question.
// But from the compiler warning, it was probably using something like
// var people = People.ToList();
// return new ObservableCollection<Person>(people);
// And the async version should be:
var people = await People.ToListAsync();
return new ObservableCollection<Person>(people);
}
}
Which you could consume as:
private async void LoadData()
{
Person = await sqlRepository.GetAllPersonAsync();
}
But I recommend consuming it via NotifyTask as described in my MVVM async data binding article. That approach would give you the ability to data-bind busy spinners and whatnot.
Should I use [Task.Run]?
No. That's "fake asynchrony" - where your code acts like it's asynchronous but it's really just synchronously running on a background thread.
I have a WPF application that initializes the state of the UI via methods in the constructor. However, it's never returning from the Wait(); in the constructor.
Here's what I am currently doing via a fairly-contrived sample:
public class SomeViewModel
{
public ICommand AddDataCommand { get { return RelayCommand(AddDataExecute); } }
public ObservableCollection<int> UIData { /* Property with INotifyPropertyChanged */}
public SomeViewModel()
{
//Load synch. here
LoadData().Wait();
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
public async void AddDataExecute()
{
//Add new data item to database on separate thread to keep UI responsive
await Task.Run(() => SomeService.AddNewData(0));
//Reload data from database and update UI, has to happen after AddNewData completes
await LoadData();
}
}
I believe it's hanging because I am never actually returning a Task. However, I don't know of a different way to assign UIData asynchronously that works both in the constructor and the Commands that call it.
You're seeing the classic deadlock situation that I describe on my blog.
To solve it, use async all the way, as I describe in my MSDN article on async best practices.
This can be difficult in some scenarios. I have a blog post describing a few approaches for async constructors. However, since you're talking about a ViewModel and data for the UI, you'll probably find my blog post on async properties more helpful - in particular, the section on data binding.
Don't construct the object through a constructor, if you require construction to be asynchronous. Use a static factory method:
public class SomeViewModel
{
private SomeViewModel()
{ }
public static async Task<SomeViewModel> Create()
{
SomeViewModel model = new SomeViewModel();
await model.LoadData();
return model;
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
//...
}
As for why your code isn't working, you're getting the standard await deadlock in which you're blocking on an asynchronous operation, that blocking is preventing continuations from being called on the UI thread, and with two different operations each waiting on the other to continue, you get a deadlock. This is why it's important to "async all the way up" rather than synchronously blocking on an asynchronous operation.
Please consider the code as shown below. By calling GetBrands, property Brands will be assigned with proper data.
public class BrandsViewModel : ViewModelBase
{
private IEnumerable<Brand> _brands;
public IEnumerable<Brand> Brands
{
get { return _brands; }
set { SetProperty(ref _brands, value); }
}
public async void GetBrands()
{
// ......
Brands = await _dataHelper.GetFavoriteBrands();
// ......
}
}
But if I test it as shown below, the test failed. How do I wait for the async call inside method GetBrands?
[TestMethod]
public void AllBrandsTest()
{
BrandsViewModel viewModel = new BrandsViewModel();
viewModel.GetBrands();
Assert.IsTrue(viewModel.Brands.Any());
}
The simple answer here is: don't make it an async void. In fact, don't ever make something an async void unless it absolutely has to be to work as an event-handler. The things that async void loses are precisely the things that you want here for your test (and presumably for your real code).
Make it an async Task method instead, and you now have the ability to wait for completion (with timeout) / add a continuation, and to check whether it exited with success or an exception.
This is a single word change, to:
public async Task GetBrands()
{
// ......
Brands = await _dataHelper.GetFavoriteBrands();
// ......
}
and then in the test:
[TestMethod]
public async Task AllBrandsTest()
{
BrandsViewModel viewModel = new BrandsViewModel();
var task = viewModel.GetBrands();
Assert.IsTrue(task.Wait(YOUR_TIMEOUT), "failed to load in time");
Assert.IsTrue(viewModel.Brands.Any(), "no brands");
}
Your model (a DTO) is populating itself (data access). This is too much for one class to do. Usually when you ask yourself "How on earth can I test this", it's time for refactoring. Create a separate data access class:
BrandsViewModel viewModel = new BrandsViewModel();
var brandAccess = new BrandsDataAccess();
viewModel.Brands = await brandAccess.GetAllBrands();
Assert.IsTrue(viewModel.Brands.Any());
Now you can test BrandsDataAccess.GetAllBrands().