I would like to load some Data before I Render my Blazor Application because in depndency to the loaded data I would like to render my app (layout, navbar ...)
Now I want to use the OnInitialised method instead of OnInitialisedAsync and with no async and await keywords.
But now I had a problem to convert the data which I get back from my API.
protected override void OnInitialized()
{
try
{ Console.WriteLine("Test1Mainasync");
LoadCategories();
}
catch (Exception e)
{
jsRuntime.ToastrError(e.Message);
}
}
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
SD.Categories = CategoriesInit1.ToList();
//foreach(var categorie in CategoriesInit){
// SD.Categories.Append(categorie);
//}
Console.WriteLine("Test1Main");
}
Has someone an idea why this converting issues happen?
I think you have this method:
public async Task<IEnumerable<CategorieDTO>> GetAllCategories()
and you should call it this way:
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
and:
protected override async Task OnInitializedAsync()
{
try
{ Console.WriteLine("Test1Mainasync");
await LoadCategories();
}
Has someone an idea why this converting issues happen?
In your code CatagiesInit1 is a Task, it's not a List<CategorieDTO>. You only get the List<CategorieDTO> when the task completes which you have no control over as you don't await the completion of the Task. In all likelyhood, your sync code will run to completion before that happens.
If your CategoryService returns a Task then the code that handles it must be async code. You can't escape from the async world back into the sync world without consequencies. If you want to live in the sync world then all the data pipeline code also needs to be blocking sync code.
If I understand your comments correctly, you want nothing to render until a certain set of conditions are met. If so add some standard Loading... component code to the page if it's page specific or App.razor if it's on initial load, or say MainLayout if it's application wide.
Here's a quick an dirty example:
<Router AppAssembly="#typeof(App).Assembly">
<Found Context="routeData">
#if (Loaded)
{
<RouteView RouteData="#routeData" DefaultLayout="#typeof(MainLayout)" />
<FocusOnNavigate RouteData="#routeData" Selector="h1" />
}
else
{
<div class="m-2 p-5 bg-secondary text-white">
<h3>Loading.....</h3>
</div>
}
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="#typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
#code {
private bool Loaded;
protected override async Task OnInitializedAsync()
{
Loaded = false;
// simulate getting the data first
await Task.Delay(5000);
Loaded = true;
}
}
Your call to API endpoint return an awaitable task but not the IEnumerable, So you can not assign awaitable task to IEnumerable so this piece of code wont work
private void LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = categorieService.GetAllCategories();
}
You should have your LoadCategories function like this
private async Task LoadCategories()
{
IEnumerable<CategorieDTO> CategoriesInit1 = new List<CategorieDTO>();
CategoriesInit1 = await categorieService.GetAllCategories();
}
API calls should be awaitable, else it will stuck your UI
You can use this solution as well
private void LoadCategories()
{
var t = Task.Run(() => categorieService.GetAllCategories()()).GetAwaiter();
t.OnCompleted(() =>
{
CategoriesInit1 = t.GetResult();
// you may need to call statehaschanged as well
StateHasChanged();
});
}
Related
I'm getting started with WinUI3. Having no previous experience with UWP or WPF or even much C#, it's tough going.
Today's question is how to display a simple dialog at startup. Consider that we start with a simple app, as generated by Visual Studio. We have a MainWindow class defined in MainWindow.xaml.cs and its associated XAML (MainWindow.xaml). I believe the class is called a code-behind class.
So I want to do something as simple as display a dialog when the (desktop) app runs. It looks as though a ContentDialog is the way to go. But how to display it? As I understand it, I'm going to need to set the XamlRoot, so naively I tried this:
public MainWindow()
{
this.InitializeComponent();
DisplayDialog();
}
private async void DisplayDialog()
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.Content.XamlRoot; // <-- set the XAML root here, but it's null
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
This doesn't work. When called, the main window's XAML root is null and trying to show the dialog throws an exception:
How do I detect when it's ok to use the main window's XAML root? This issue seems to hint at an OnLoaded event, but I can't find anything about OnLoaded events in WinUI. Did I miss something?
This only way I can get this to work is to hook into the window's button and respond to its Loaded event, i.e.
public MainWindow()
{
this.InitializeComponent();
myButton.Loaded += MyButton_Loaded; // <-- hack
}
private void MyButton_Loaded(object sender, RoutedEventArgs e)
{
DisplayDialog();
}
private async void DisplayDialog()
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.Content.XamlRoot; // <-- this is non-null now!
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
But this feels really dirty. I don't even know if it's guaranteed that the XamlRoot will be non-null just because a button has loaded. And anyway, latching onto the button load seems very much like a hack. It relies on there being a button for one thing!
So how should I achieve the simple task of putting a dialog on the screen when all I have is the main window?
All help very gratefully received. Please try to make any answers as newbie-friendly as possible.
Unfortunately, the MainWindow has no Loaded events. What you can do is to work with a Page instead. So basically use the MainWindow as a "Window" and use Pages for your contents.
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.XamlRoot;
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
}
MainWindow.xaml
<Window
x:Class="ContentDialogs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:ContentDialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:MainPage />
</Window>
A WinUI Window is just an abstraction of each of the low-level window implementations used by supported (UWP and desktop) app models.
The "trick" is to handle the Loaded event of the window's root element. The default template includes a Grid root element. A "generic" solution could be implemented something like this:
public MainWindow()
{
this.InitializeComponent();
var root = this.Content as FrameworkElement;
if (root != null)
root.Loaded += async (s, e) => await DisplayDialog();
}
private async Task DisplayDialog()
{
var dlg = new ContentDialog();
dlg.XamlRoot = this.Content.XamlRoot;
dlg.Content = "Hello World";
await dlg.ShowAsync();
}
Also tote that an async method, with the exception of event handlers, should return a Task or a Task<T> and be awaited by the caller.
I created a .net core web api project. It has gotten kinda big and I want to program a "delete" operation which deletes a lot of stuff from the database. Since there are a lot of things to delete, this will be a long running process. So I thought maybe I can run this in the background and just write status updates somewhere for the user to see whats happening.
I googled this and I found BackgroundWorkerQueue and thought this might be my solution.
So I registered the service and everything and here is my method that calls it:
public class DeleteController : ControllerBase {
private readonly BackgroundWorkerQueue _backgroundWorkerQueue;
public AdminController(BackgroundWorkerQueue backgroundWorkerQueue){
_backgroundWorkerQueue = backgroundWorkerQueue;
}
public async Task<ActionResult> HugeDeleteMethod(int id)
{
// some prechecks here...
// and here I thought I'd start the background task
_backgroundWorkerQueue.QueueBackgroundWorkItem(async token =>
{
var a = _context.StatusTable.Find(id);
a.Status += "Blablablabla\n";
_context.StatusTable.Update(a);
await _context.SaveChangesAsync();
//now start doing delete operations
});
}
}
And that class looks like this:
public class BackgroundWorkerQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems = new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public async Task<Func<CancellationToken, Task>> DequeueAsync(CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
public void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
}
There is also a DeleteService, which is also called in my startup, but I am not sure what it does:
public class DeleteService : BackgroundService
{
private readonly BackgroundWorkerQueue queue;
public NukeService(BackgroundWorkerQueue queue)
{
this.queue = queue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await queue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
}
}
Both are added in my startup.cs:
services.AddHostedService<DeleteService>();
services.AddSingleton<BackgroundWorkerQueue>();
Well, maybe I'm going about this all wrong. This is never called it seems, the StatusTable field "Status" is always empty. So how do I do this?
You just need to subclass BackgroundService class or implement IHostedService and than register your service as hosted service.
This will run a service in the background. Than in your service you can leverage the BlockingQueue that will perform tasks only when they are added, e.g. like this:
public class MyService : BackgroundService {
private readonly BlockingCollection<long> queue;
public MyService(){
this.queue = new BlockingCollection<long>();
Task.Run(async () => await this.Execute());
}
public void AddId(long id) {
this.queue.Add(id);
}
private async Task Execute()
{
foreach (var id in this.queue.GetConsumingEnumerable())
{
... do your stuff ...
}
}
}
services.AddHostedService<MyService>();
Here is the docu: Background services in .net core
In my app I have to be able to cancel uploads, I tried with threads cancelation but nothing happens, I think it is because I use DBContextFactory and I create a context for each uploaded file.
So I did this to save files in DB:
private async Task OnFilesDropped(FileUploadModel upload)
{
Uploads.Add(upload);
if (string.IsNullOrEmpty(upload.Error))
{
using var context = DbFactory.CreateDbContext();
upload.Context = context;
context.FileUploads.Add(upload);
upload.UploadCompletion = 45;
await context.SaveChangesAsync();
upload.UploadCompletion = 100;
}
}
and this in case of deleting a uploaded/uploading file:
private async Task DeleteUpload(FileUploadModel upload)
{
Uploads.Remove(upload);
await UploadsChanged.InvokeAsync(Uploads);
if (string.IsNullOrEmpty(upload.Error))
{
if (upload.UploadCompletion != 100)
{
await upload.Context.DisposeAsync();
}
else
{
using var context = DbFactory.CreateDbContext();
context.FileUploads.Remove(upload);
await context.SaveChangesAsync();
}
}
}
This way works because I dispose of the context, but I wonder if there is a better way of doing this? or if this solution could be problematic somehow?
Best Regards.
You should use a CancellationToken.
The SaveChangesAsync method on your context has an overload that can be provided a cancellationToken.
await context.SaveChangesAsync(cancellationToken);
If you already have a CancellationToken higher in the call stack, you can just pass that one down. Otherwise, you can create a CancellationTokenSource and use that to generate a cancellation token and then cancel it when appropriate.
I am using MVVMCross with my cross-platform Windows Phone and Android app. In the core project's main view model, I am doing some background work using TPL and I want to make sure that in the callback, when I make changes to the properties of the view model which will trigger UI change, that the code is run on UI thread, how do I achieve this?
For code, here is how it likes
private MvxGeoLocation _currentLocation;
private Task<MvxGeoLocation> GetCurrentLocation()
{
return Task.Factory.StartNew(() =>
{
while (_currentLocation == null && !LocationRetrievalFailed)
{
}
return _currentLocation;
});
}
var location = await GetCurrentLocation();
if (LocationRetrievalFailed)
{
if (location == null)
{
ReverseGeocodingRequestFailed = true;
return;
}
// Show toast saying that we are using the last known location
}
Address = await GooglePlaceApiClient.ReverseGeocoding(location);
Did you try IMvxMainThreadDispatcher?
var dispatcher = Mvx.Resolve<IMvxMainThreadDispatcher>();
dispatcher.RequestMainThreadAction(()=> { .... });
See more on the implementation:
https://github.com/MvvmCross/MvvmCross/search?q=IMvxMainThreadDispatcher&type=Code
Usually I don't think you need this though.
Since you start the async processing from main thread, the async operations should return back to main thread.
Can you give an example of the async code you are doing?
Update on 24th August 2020:
As #claudio-redi has mentioned, ExecuteOnMainThreadAsync needs to be used. But Mvx.Resolve is now obsolete. So the latest snippet would be:
var mainThreadAsyncDispatcher = Mvx.IoCProvider.Resolve<IMvxMainThreadAsyncDispatcher>();
await mainThreadAsyncDispatcher.ExecuteOnMainThreadAsync( async ()=> { await SomeAsyncTask() });
Method RequestMainThreadAction is now obsolete. Today you have to do
var dispatcher = Mvx.Resolve<IMvxMainThreadAsyncDispatcher>();
await dispatcher.ExecuteOnMainThreadAsync(()=> { .... });
The 'standard' CSLA async server calls have typically been structured per the following:
Base class:
public static void GetMyObject(EventHandler<DataPortalResult<MyObject>> callback) {
var dp = new DataPortal<MyObject>();
dp.FetchCompleted += callback;
dp.BeginFetch();
}
ViewModel:
protected override void OnInitialize(object parameter) {
base.OnInitialize(parameter);
base.IsBusy = true;
MyObject.GetMyObject((o, e) => {
if (HasNoException(e)) {
Model = e.Object;
}
base.IsBusy = false;
});
}
With the new async/await features, the format would be something like this:
public async static Task<MyObject> GetMyObject() {
return await DataPortal.FetchAsync<MyObject>();
}
and
protected async override void OnInitialize(object parameter) {
base.OnInitialize(parameter);
base.IsBusy = true;
Model = await MyObject.GetMyObjectAsync();
base.IsBusy = false;
}
Should the callback pattern be considered deprecated at this point, or is it still useful for certain UI technologies? When doing a new project, I'd rather not have those methods in there if I can help it.
Personally, I prefer TAP methods over callbacks in all my code.
With Microsoft.Bcl.Async, most platforms support async. However, there are a few situations where neither TAP nor Task is available, e.g., Windows Phone 7.0, .NET CF, SL 3. I would only use callbacks if I had to support one of those platforms.