Perform synchronous operation on Ui thread - c#

I am trying develop a Windows App and run into issues.
I have a MainPage.xaml and 2 others StartScreen.xaml and Player.xaml.
I am switching content of the MainPage if certain conditions are true.
So I have an event in StartScreen it checks if a directory exist or not but it throws me every time an error.
private void GoToPlayer_Click(object sender, RoutedEventArgs e)
{
if (Directory.Exists(this.main.workingDir + "/" + IDText.Text + "/Tracks")) // Error occurs here
{
this.main.Content = this.main.player; //here i switch between different ui forms
}
else
{
MessageBox.Text = "CD not found";
IDText.Text = "";
}
}
When it hit the else branch everything is fine but when the dir is available I get the following error message:
An exception of type 'System.InvalidOperationException' occurred in System.IO.FileSystem.dll but was not handled in user code
Additional information: Synchronous operations should not be performed on the UI thread. Consider wrapping this method in Task.Run.
Even if I commenting the code in the if branch out the error still comes.
I tried this:
private async void GoToPlayer_Click(object sender, RoutedEventArgs e)
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
if (Directory.Exists(this.main.workingDir + "/" + IDText.Text + "/Tracks")) // Error occurs here
{
this.main.Content = this.main.player; //here i switch between different ui forms
}
else
{
MessageBox.Text = "CD not found";
IDText.Text = "";
}
});
}
Still the same error, as I understood this should be run asynchronous and wait until the code complete, but it doesn't seems so. I also tried bunch other stuff but still get the errors.
I don't know how to fix that, could someone please explain why this is happening and how to fix that.

As the exception says, you are not allowed to call Directory.Exists synchronously in the UI thread. Putting the whole code block in a Dispatcher action still calls it in the UI thread.
In a UWP app you would usually use the StorageFolder.TryGetItemAsync method to check if a file or folder exists:
private async void GoToPlayer_Click(object sender, RoutedEventArgs e)
{
var folder = await StorageFolder.GetFolderFromPathAsync(main.workingDir);
if ((folder = await folder.TryGetItemAsync(IDText.Text) as StorageFolder) != null &&
(folder = await folder.TryGetItemAsync("Tracks") as StorageFolder) != null)
{
...
}
}
Note that you may still get an UnauthorizedAccessException when the application is not allowed to access main.workingDir.

[Update July 22 2018 with example]
The error message tells you everything you need to know:
Consider wrapping this method in Task.Run
You should wrap the code in a call to Task.Run. This will ensure it runs on a background thread.
Here is a simple example:
var picker = new FolderPicker();
picker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
picker.FileTypeFilter.Add(".mp3");
var folder = await picker.PickSingleFolderAsync();
var result = await Task.Run(() => Directory.Exists(Path.Combine(folder.Path, "foobar")));
if (result)
{
Debug.WriteLine("Yes");
}
else
{
Debug.WriteLine("No");
}
Background information in case anyone cares:
I assume, based on your sample code, that you are reading the Music Library. In this case, the .NET file APIs go through the WinRT APIs under the covers since it is a brokered location. Since the underlying WinRT APIs are async, .NET has to do work to give you the illusion of synchronous behaviour, and they don't want to do that on the UI thread.
If you were only dealing with your own local data folders, the .NET APIs would use the underlying Win32 APIs (which are synchronous already) and so would work without any background thread requirements.
Note that on my machine, this appears to work even on the UI thread, so it might depend on the version of Windows that you are targeting.

Related

Blazor - A second operation was started on this context instance before a previous operation completed

I am currently working on building a scanning portion of an app, and noticed that when my eventlistener hits and executes a function, I eventually get the following error:
A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
After doing some research here, I learned that I should probably be awaiting those processes, but what I've added doesn't seem to be doing the trick. My threads are still getting tangled. My console shows the issue occuring specifically at:
GetByBarcode(String barcode)
But I'm not sure where else I would need to account for an await operator in that task. Am I missing an await call somewhere, or could the issue actually big bigger than that? Perhaps I've misunderstood await. I've added the relevant portions of code below.
<div #onkeydown="#KeyDown"
tabindex="0"
#ref="scanner">
public async Task KeyDown(KeyboardEventArgs e)
{
barcode += e.Key;
await CheckBarcode();
}
private async Task CheckBarcode()
{
testStuff = await stuffRepository.GetByBarcode(barcode);
if(testStuff is null)
{
//
}
else
{
await stuffRepository.Update(testStuff, loginID);
await LoadStuff();
barcode = "";
}
}
public async Task<StuffDTO> GetByBarcode(string barcode)
{
var obj = await _db.StuffTable.FirstOrDefaultAsync(u => u.Barcode == barcode);
if (obj != null)
{
return _mapper.Map<Stuff, StuffDTO>(obj);
}
return new StuffDTO();
}
In the async world you can be awaiting one DbContext operation and trying to start another at the same time. That's what the error is telling you.
You should switch to using the DbContextFactory and separate contexts per operation. The DbContextFactory manages a pool of connections and deals with create and disposing of individual contexts. See this MS article that explains how to configure it - https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor.
You should also be building some form of data pipeline to interact with your database, not using Db contexts directly in your components.

Async/Await Usage in .NET

I've been working on a project and saw the below code. I am new to the async/await world. As far as I know, only a single task is performing in the method then why it is decorated with async/await. What benefits I am getting by using async/await and what is the drawback if I remove async/await i.e make it synchronous I am a little bit confused so any help will be appreciated.
[Route("UpdatePersonalInformation")]
public async Task<DataTransferObject<bool>> UpdatePersonalInformation([FromBody] UserPersonalInformationRequestModel model)
{
DataTransferObject<bool> transfer = new DataTransferObject<bool>();
try
{
model.UserId = UserIdentity;
transfer = await _userService.UpdateUserPersonalInformation(model);
}
catch (Exception ex)
{
transfer.TransactionStatusCode = 500;
transfer.ErrorMessage = ex.Message;
}
return transfer;
}
Service code
public async Task<DataTransferObject<bool>> UpdateUserPersonalInformation(UserPersonalInformationRequestModel model)
{
DataTransferObject<bool> transfer = new DataTransferObject<bool>();
await Task.Run(() =>
{
try
{
var data = _userProfileRepository.FindBy(x => x.AspNetUserId == model.UserId)?.FirstOrDefault();
if (data != null)
{
var userProfile = mapper.Map<UserProfile>(model);
userProfile.UpdatedBy = model.UserId;
userProfile.UpdateOn = DateTime.UtcNow;
userProfile.CreatedBy = data.CreatedBy;
userProfile.CreatedOn = data.CreatedOn;
userProfile.Id = data.Id;
userProfile.TypeId = data.TypeId;
userProfile.AspNetUserId = data.AspNetUserId;
userProfile.ProfileStatus = data.ProfileStatus;
userProfile.MemberSince = DateTime.UtcNow;
if(userProfile.DOB==DateTime.MinValue)
{
userProfile.DOB = null;
}
_userProfileRepository.Update(userProfile);
transfer.Value = true;
}
else
{
transfer.Value = false;
transfer.Message = "Invalid User";
}
}
catch (Exception ex)
{
transfer.ErrorMessage = ex.Message;
}
});
return transfer;
}
What benefits I am getting by using async/await
Normally, on ASP.NET, the benefit of async is that your server is more scalable - i.e., can handle more requests than it otherwise could. The "Synchronous vs. Asynchronous Request Handling" section of this article goes into more detail, but the short explanation is that async/await frees up a thread so that it can handle other requests while the asynchronous work is being done.
However, in this specific case, that's not actually what's going on. Using async/await in ASP.NET is good and proper, but using Task.Run on ASP.NET is not. Because what happens with Task.Run is that another thread is used to run the delegate within UpdateUserPersonalInformation. So this isn't asynchronous; it's just synchronous code running on a background thread. UpdateUserPersonalInformation will take another thread pool thread to run its synchronous repository call and then yield the request thread by using await. So it's just doing a thread switch for no benefit at all.
A proper implementation would make the repository asynchronous first, and then UpdateUserPersonalInformation can be implemented without Task.Run at all:
public async Task<DataTransferObject<bool>> UpdateUserPersonalInformation(UserPersonalInformationRequestModel model)
{
DataTransferObject<bool> transfer = new DataTransferObject<bool>();
try
{
var data = _userProfileRepository.FindBy(x => x.AspNetUserId == model.UserId)?.FirstOrDefault();
if (data != null)
{
...
await _userProfileRepository.UpdateAsync(userProfile);
transfer.Value = true;
}
else
{
transfer.Value = false;
transfer.Message = "Invalid User";
}
}
catch (Exception ex)
{
transfer.ErrorMessage = ex.Message;
}
return transfer;
}
The await keyword only indicates that the execution of the current function is halted until the Task which is being awaited is completed. This means if you remove the async, the method will continue execution and therefore immediately return the transfer object, even if the UpdateUserPersonalInformation Task is not finished.
Take a look at this example:
private void showInfo()
{
Task.Delay(1000);
MessageBox.Show("Info");
}
private async void showInfoAsync()
{
await Task.Delay(1000);
MessageBox.Show("Info");
}
In the first method, the MessageBox is immediately displayed, since the newly created Task (which only waits a specified amount of time) is not awaited. However, the second method specifies the await keyword, therefore the MessageBox is displayed only after the Task is finished (in the example, after 1000ms elapsed).
But, in both cases the delay Task is ran asynchronously in the background, so the main thread (for example the UI) will not freeze.
The usage of async-await mechanism mainly used
when you have some long calculation process which takes some time and you want it to be on the background
in UI when you don't want to make the main thread stuck which will be reflected on UI performance.
you can read more here:
https://learn.microsoft.com/en-us/dotnet/csharp/async
Time Outs
The main usages of async and await operates preventing TimeOuts by waiting for long operations to complete. However, there is another less known, but very powerful one.
If you don't await long operation, you will get a result back, such as a null, even though the actual request as not completed yet.
Cancellation Tokens
Async requests have a default parameter you can add:
public async Task<DataTransferObject<bool>> UpdatePersonalInformation(
[FromBody] UserPersonalInformationRequestModel model,
CancellationToken cancellationToken){..}
A CancellationToken allows the request to stop when the user changes pages or interrupts the connection. A good example of this is a user has a search box, and every time a letter is typed you filter and search results from your API. Now imagine the user types a very long string with say 15 characters. That means that 15 requests are sent and 15 requests need to be completed. Even if the front end is not awaiting the first 14 results, the API is still doing all the 15 requests.
A cancellation token simply tells the API to drop the unused threads.
I would like to chime in on this because most answers although good, do not point to a definite time when to use and when not.
From my experience, if you are developing anything with a front-end, add async/await to your methods when expecting output from other threads to be input to your UI. This is the best strategy for handling multithread output and Microsoft should be commended to come out with this when they did. Without async/await you would have to add more code to handle thread output to UI (e.g Event, Event Handler, Delegate, Event Subscription, Marshaller).
Don't need it anywhere else except if using strategically for slow peripherals.

Windows Store App with c#: how to load data from a fily (async) into UI?

I'm developing a Windows 10 Universal App. There is a RichEditBox and a ListView. Each time the User click an ListView-item the App should save the actual Content of the RichEditBox into a rtf file and load data from another file to display it in the RichEditBox.
What's confusing to me is how to deal with asyc methods in the context of UI Calls. So, what will be "nice" code to accomplish that task?
Basically I have a click method, that first calls the save method and then calls the load method. But... Should that click method be async? Should the load / save be async (i guess, because typically IO operations need to be awaited).
First thing i tried was this:
private void MasterListView_ItemClick(object sender, ItemClickEventArgs e)
{
// don't care that UI will not wait on completion
saveRtfFile("name");
loadRtfFile("name");
}
private async Task saveRtfFile(String filename)
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile isfStorageFile = await localFolder.GetFileAsync(filename);
if (isfStorageFile != null)
{
// Prevent updates to the remote version of the file until we
//finish making changes and call
Windows.Storage.CachedFileManager.DeferUpdates(isfStorageFile);
// write to file
Windows.Storage.Streams.IRandomAccessStream randAccStream = await isfStorageFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
MyRichEditBox.Document.SaveToStream(Windows.UI.Text.TextGetOptions.FormatRtf, randAccStream);
// finished changing -> other app can update the file.
FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(isfStorageFile);
if (status != FileUpdateStatus.Complete)
{
Windows.UI.Popups.MessageDialog
errorBox = new Windows.UI.Popups.MessageDialog("File " + isfStorageFile.Name + " couldn't be saved.");
await errorBox.ShowAsync();
}
}
}
private async Task loadRtfFile(String filename)
{
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile rtfStorageFile = await localFolder.GetFileAsync(filename);
if (rtfStorageFile != null)
{
Windows.Storage.Streams.IRandomAccessStream randAccStream = await rtfStorageFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
// Load the file into the Document property of the RichEditBox.
MyRichEditBox.Document.LoadFromStream(Windows.UI.Text.TextSetOptions.FormatRtf, randAccStream);
}
}
I guess that my "solution" is complete crap, since it throws fore some reason errors.
What is the correct way of handling the asynchronous IO calls inside the click method?
As a general rule, you should do "async all the way" - that is, when you call a method, you should (sooner or later) await the task it returns. When you follow this principle, your event handler becomes:
private async void MasterListView_ItemClick(object sender, ItemClickEventArgs e)
{
await saveRtfFileAsync("name");
await loadRtfFileAsync("name");
}
This will finish saving the file before loading it. The original code was starting to save and then trying to load before waiting for the save to complete, which may have caused your error.

Wait for BackgroundTask to complete

I have a BackgroundTask which generates some text and then saves it as a file to a LocalFolder. I need to get this file with my main project (same VS solution) after it's been generated by the BackgroundTask and do some next work with it. The background task is triggered both manually by the user (via a "Reload" button) and every 15 mins by a TimeTrigger.
This is the relevant code snippet:
syncTrigger.RequestAsync();
articles = await getCachedArticles("");
How can I tell the getCachedArticles method to wait until after previous request finishes before running? Thank you very much!
If I understand it correctly, what you need to do is to wait for the BackgroundTaskRegistration.Completed event to fire.
One approach would be to create an extension method that returns a Task that completes when the event fires:
public static Task<BackgroundTaskCompletedEventArgs> CompletedAsync(
this BackgroundTaskRegistration registration)
{
var tcs = new TaskCompletionSource<BackgroundTaskCompletedEventArgs>();
BackgroundTaskCompletedEventHandler handler = (s, e) =>
{
tcs.SetResult(e);
registration.Completed -= handler;
};
registration.Completed += handler;
return tcs.Task;
}
You would then use it like this:
var taskCompleted = registration.CompletedAsync();
await syncTrigger.RequestAsync();
await taskCompleted;
articles = await getCachedArticles("");
Note that the code calls CompletedAsync() before calling RequestAsync() to make sure the even handler is registered before the task is triggered, to avoid the race condition where the task completes before the handler is registered.
Whenever you want your current context to wait for an asynchronous method to complete before continuing, you want to use the await keyword:
await syncTrigger.RequestAsync(); //the line below will not be executed until syncTrigger.RequestAsync() completes its task
articles = await getCachedArticles("");
I would recommend reading through the await C# Reference Article in order to get the full picture of how it works.
EDIT: svick's answer shows the best approach, I even wrote a blog post about it. Below is my original answer with a couple of indirect alternatives that might work for some cases.
As others have noted, awaiting syncTrigger.RequestAsync() won't help, although it is a good idea nevertheless. It will resume execution when the background task was succesfully triggered, and as such allow you to check if it failed for any reason.
You could create an app service to make it work when you are triggering the background task from the application. App services behave similar to web services. They are running in a background task, but have request-response semantics.
In the background service you would need to handle the RequestReceived event:
public void Run(IBackgroundTaskInstance taskInstance)
{
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceconnection = details.AppServiceConnection;
appServiceconnection.RequestReceived += OnRequestReceived;
}
private async void OnRequestReceived(AppServiceConnection sender,
AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
ValueSet arguments = args.Request.Message;
ValueSet result = new ValueSet();
// read values from arguments
// do the processing
// put data for the caller in result
await args.Request.SendResponseAsync(result);
messageDeferral.Complete();
}
In the client you can then call the app service:
var inventoryService = new AppServiceConnection();
inventoryService.AppServiceName = "from manifest";
inventoryService.PackageFamilyName = "from manifest";
var status = await inventoryService.OpenAsync();
var arguments = new ValueSet();
// set the arguments
var response = await inventoryService.SendMessageAsync(arguments);
if (response.Status == AppServiceResponseStatus.Success)
{
var result = response.Message;
// read data from the result
}
Check the linked page for more information about app services.
You could call the same app service from the scheduled background task as well, but there would be no way to get notified when the processing was completed in this case.
Since you've mentioned that you're exchanging data via a file in LocalFolder your application could try monitoring the changes to that file instead:
private async Task Init()
{
var storageFolder = ApplicationData.Current.LocalFolder;
var monitor = storageFolder.CreateFileQuery();
monitor.ContentsChanged += MonitorContentsChanged;
var files = await monitor.GetFilesAsync();
}
private void MonitorContentsChanged(IStorageQueryResultBase sender, object args)
{
// react to the file change - should mean the background task completed
}
As far as I know you can only monitor for all the changes in a folder and can't really determine what changed inside the event handler, so for your case it would be best to have a separate sub folder containing only the file saved by the background task once it completes. This way the event would only get raised when you need it to.
You'll have to check for yourself whether this approach works reliably enough for you, though.
You need to wait the RequestAsync method completed with the result
https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.background.devicetriggerresult
after that I suggest to wait a few seconds with task.delay and try to get the data.
Update:
When you have the device trigger result you need to check this result before to try to get the data after that you can suppose that you have the data saved. I suggested before to use Task.Delay just to wait a few seconds to be sure all data is saved because sometimes the process take some milliseconds more than is expected. I did this because we don't have an event like TriggerCompleted I needed to create my own approach.
I did this before in my app and it works very well.

WinJS - WinRT Component - The application called an interface that was marshalled for a different thread

I'm working on a WinRT component in C# which I'm calling from WinJS asyncronously.
I'm calling a library which when called, throws The application called an interface that was marshalled for a different thread exception. I understand this to be an issue with the thread that the library code is running on, via the UI thread the JS code is running under.
I've found some threads on here which mention ways this can potentially work ( Run code on UI thread in WinRT etc ) but none of these touch on calling code from WinJS, they all tend to be XAML.
So, I'm hoping to answer two questions:
At a high level, should I by trying to make the WinRT library code run on the UI thread? thereby making it syncronous? if that is correct, what is the most effective way to make my WinRT code which handles writing to the file system, behave in this way (code below)
RT Code
public IAsyncOperation<string> ButtonPress()
{
return SaveSpreadsheet().AsAsyncOperation();
}
private async Task<string> SaveSpreadsheet()
{
var res = false;
try
{
CreateSpreadsheet();
AddSomeContentToASheet();
var folder = KnownFolders.DocumentsLibrary;
var outFile = await folder.CreateFileAsync("New.xls", CreationCollisionOption.ReplaceExisting);
res = await book.SaveAsAsync(outFile, ExcelSaveType.SaveAsXLS);
book.Close();
engine.Dispose();
}
catch (Exception e)
{
return e.Message;
}
return "Success! " + res;
}
JS Code
button.onclick = function () {
var rtComponent = new WindowsRuntimeComponent1.Class1();
rtComponent.buttonPress().then(function (res) {
document.getElementById('res').innerText = "Export should have: " + res;
});
};
If 1 is wrong and I should by trying to leave the RT code async but running on the same thread as my JS UI code, how can I get a reference to that thread, and then kick off the methods in the RT code? I've tried some Dispatcher.RunAsync() ways of running the RT code, but I come unstuck when I need to get a reference to an IStorageFile through the WinRT framework methods.
Any thoughts greatly appreciated.
I believe what you're missing is a call to Task.Run, which is what actually creates the Task that will run the operation on a separate thread and do the marshaling.
That is, your SaveSpreadsheet function says it's returning Task but doesn't actually create the Task anywhere, and is thus just returning a string. Here's the general structure of an async function in a component:
public IAsyncOperation<string> SomeMethodAsync(int id)
{
var task = Task.Run<string>(async () =>
{
var idString = await GetMyStringAsync(id);
return idString;
});
return task.AsAsyncOperation();
}
So I think you just need to change SaveSpreadsheet to return a String (which your code is doing already, so just change Task to String), and make ButtonPress look like this:
public IAsyncOperation<string> ButtonPress()
{
var task = Task.Run<string>(async () =>
{
return await SaveSpreadsheet();
});
return task.AsAsyncOperation();
}
I cover this subject, by the way, in Chapter 18 of my free ebook from MSPress, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, http://aka.ms/BrockschmidtBook2.
P.S. If there's no reason for your component to be instantiable with new, then you can add the static keyboard to ButtonPress (i.e. public static IAsyncOperation ButtonPress()) and then just make the call with the fully-qualified name WindowsRuntimeComponent1.Class1.buttonPress().then(...). That might simplify your code.

Categories

Resources