Call async method from non-async method in blazor web assembly - c#

I'm trying to develop an extensible application with Blazor WASM, which downloads plugins from server, and caches them at browser storage.
Both downloading and caching APIs are async.
The following AssemblyLoadContext, is responsible to load plugin assemblies.
As plugins may contain multiple assembly files, and as the project should support lazy loading of referenced assemblies, I'm handling the Resolving event as follow:
public class PluginAssemblyLoadContext : AssemblyLoadContext
{
private readonly IPluginResolver? _pluginResolver;
public PluginAssemblyLoadContext(string name) : this(name, null)
{
}
public PluginAssemblyLoadContext(string name, IPluginResolver? pluginResolver) : base(name, isCollectible: true)
{
_pluginResolver = pluginResolver;
if (pluginResolver != null)
{
Resolving += ResolveAssembly;
}
}
protected override Assembly? Load(AssemblyName name)
{
return null;
}
private Assembly? ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
{
try
{
var assembly = AssemblyLoadContext.Default.Assemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name);
if (assembly == null)
{
var task = _pluginResolver?.ResolveAsync(context, assemblyName);
if (task != null)
{
task.Wait(); // <- The problem!
assembly = task.Result; // <- The problem!
}
}
if (assembly == null)
{
// TODO: Log...
}
return assembly;
}
catch (Exception ex)
{
// TODO: Log...
throw;
}
}
}
The problem is that I can't call the async API from this method as it throws an exception telling "Cannot wait on monitors on this runtime." and I couldn't figure out how should I call them synchronously.
Thanks for any help

It seems like you are trying to block the main execution thread by doing.Wait() call. This is bad because the main thread may be responsible for handling UI updates. If it is blocked - the UI of the application will be frozen while your task is in progress. I believe this is the main reason you are getting this error.
The solution I may propose - do not wait till the task is finished. Instead, add a callback to it via .ContinueWith(). In that callback, you may handle the result of the execution and finish your business logic (in your case - perform logging).

Is it possible to rewrite this into async-await? That's to only async way, Blazor Webassambly can handle at the moment.
Or a more dirty way:
Call a js function, that calls back
[JSInvokable] public async Task<Assembly?> GetTaskResult(Task task)
=> await task;

I'm afraid that the AssemblyLoadContext doesn't really leave you with any other option. In ASP.NET Core for example, when you want to write a middleware, you do this:
app.Use((context, next) =>
{
return next();
});
In this case the following overload is called:
Now if you need to do an async call, you can do so:
app.Use(async (context, next) =>
{
await next();
});
But this is only possible because ASP.NET Core provides you with an overload for Use (where the callback parameter is usually of type Func<Task>):
So the bottom line is: AFAIK it's not possible, as long as AssemblyLoadContext doesn't provide events with Task<Assembly> return type.
Your best bet is probably to create an issue on Github.

Related

Asp.net Core: Can not access a disposed object (different errors)

I know that this problem is here a lot, but I have to say I read everything I could found for like two days and don't get my error.
I created a ASP.net Core REST API and get always different errors:
"Can not access a disposed object.."
"An exception occurred while iterating over the results of a query
for context type.."
"A second operation started on this context before a previous
operation completed"..
Maybe someone of you sees my error or can explain to me, what I'm doing wrong.
Rest-API:
// POST api/events
[HttpPost("create")]
public async Task<IActionResult> CreateAsync([FromBody] EventDTO eventDTO)
{
var newEvent = _mapper.Map<Event>(eventDTO);
try
{
await _eventService.CreateEventAsync(newEvent);
return Ok(newEvent);
}
catch (AppException ex)
{
return BadRequest(new { message = ex.Message });
}
}
Interface:
public interface IEventService
{
Task<IEnumerable<Event>> GetAllEventsAsync();
Task<Event> GetEventByIDAsync(int id);
Task<IEnumerable<Event>> GetEventByCityAsync(string city);
Task<Event> CreateEventAsync(Event newEvent);
void UpdateEventAsync(Event newEvent, Event existing, int eventId);
void DeleteEventAsync(Event existing);
}
Eventservice:
public class EventService : IEventService
{
private MeMeContext _dbContext;
public EventService(MeMeContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Event> CreateEventAsync(Event newEvent)
{
_dbContext.Events.Add(newEvent);
await _dbContext.SaveChangesAsync();
return newEvent;
}
...
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc().
SetCompatibilityVersion(CompatibilityVersion.Version_2_2).
AddJsonOptions(opts => opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
services.AddDbContext<MeMeContext>(opts => opts.UseNpgsql(Configuration.GetConnectionString(DATABASE)));
services.AddScoped<MeMeContext>();
// configure DI for application services
services.AddScoped<IUserService, UserService>();
services.AddScoped<IEventService, EventService>();
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new AutoMapperProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
...
}
One thing that I don't understand also, is that I get different errors, when I start my application with Visual Studio or with "dotnet run". One thing that also happens from time to time is, that sometimes my code works, when I do other things on the REST API.
When you need more information, just ask. I'm happy with every hint that you can give me :)
Thanks in advance!
You're not awaiting an async method. As such, the code in the action moves on while that CreateEventAsync logic is running. When the response returns, the context goes away, since its lifetime is that scope.
In other words, you have essentially a race condition. If the CreateEventAsync logic happens to finish before the response returns, everything is fine. However, if it takes longer than returning the response, then the context is gone (along with your other scoped services), and you start throwing exceptions.
Long and short, use the await keyword:
await _eventService.CreateEventAsync(newEvent);
Async is not the same as running something in the background. If you want the action to be able to return before this logic completes, then you should schedule this to run on a background service instead. See: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio

Async method that throws exception won't resume the thread context back to the same thread

When I am using async await and an exception is thrown the thread context is being lost. In my code I'm using dependency injection that registered to resolve per thread so I need to execute my code on the same thread.
This is how it is setup:
I have a method that will try calling different communicators using async when one throws an exception it will go onto the next one:
public async Task<TResponse> VisitRequestAsync(Context context)
{
/* ....
prepare request from context
.... */
var communicatorEnumerableInstance = _communicatorService.GetCommunicatorInstanceEnumerable();
foreach (var communicator in communicatorEnumerableInstance)
{
using (communicator)
{
var communicatorInstance = communicator as ICommunicator<TResponse, TRequest>;
try
{
return await communicatorInstance.ProcessAsync(request).ConfigureAwait(true);
break;// call will break out of the for-each loop if successful processed.
}
catch (Exception exception)
{
continue;// Continue to load next communication method/instance
}
}
}
}
Below is a unit test that contains a communicator that always throws an exception and one that tries to get a dependency that is registered onto the original thread.
public class TestDependancy : ITestDependancy
{
}
public interface ITestDependancy
{ }
public class TestCommunicatorThrowsException :
ICommunicator<ResponseType, RequestType>
{
public async Task<ResponseType> ProcessAsync(RequestType request)
{
var task = Task.Run(() =>
{
throw new Exception();
return new ResponseType();
});
return await task;
}
public void Dispose()
{
}
}
public class TestCommunicatorGetsDependency :
ICommunicator<ResponseType, RequestType>
{
public TestCommunicatorGetsDependency()
{ }
public async Task<ResponseType> ProcessAsync(RequestType request)
{
TestDependancy = DefaultFactory.Default.Resolve<ITestDependancy>();
var task = Task.Run(() => new ResponseType());
return await task;
}
public ITestDependancy TestDependancy { get; set; }
public void Dispose()
{
}
}
[TestMethod]
[TestCategory("Unit")]
public async Task it_should_be_able_to_resolve_interface_from_original_thread()
{
var secondCommunicator = new TestCommunicatorGetsDependency();
_communicators = new ICommunicator<ResponseType, RequestType>[]
{new TestCommunicatorThrowsException(), secondCommunicator};
_communicatorServiceMock.Setup(
x => x.GetCommunicatorInstanceEnumerable(It.IsAny<string>(), It.IsAny<string>()))
.Returns(_communicators);
((IFactoryRegistrar) DefaultFactory.Default).RegisterPerThread<ITestDependancy, TestDependancy>();
var firstInstance = DefaultFactory.Default.Resolve<ITestDependancy>();
await it.VisitRequestAsync(_context).ConfigureAwait(true);
var secondInstance = secondCommunicator.TestDependancy;
Assert.AreEqual(firstInstance, secondInstance);
}
When the dependencies are resolved in the unit test they are not equal. After looking into it I see that the value for CurrentThread.ManagedThreadId changes at the point when the exception gets thrown. Then when it is caught in the VistRequestAsync method the CurrentThread.ManagedThreadId is never restored to its original state. So then the dependency injection is unable to get the same instance because it is now operating on a different thread.
Originally, I was using .ConfigureAwait(false) with the await. Then I tried setting it to true and I started seeing it sometimes get the same thread back. Which sounds a lot like what is said in this answer.
This post about the synchronization context and async sounds a lot like the problem I am facing. My trouble is I'm using WebApi and need a response back when things get done so I'm not sure how to use his message pump and asynchronously wait for an answer.
Async uses the ThreadPool to process tasks. This means that there is no guarantee that an async operation will start and complete on the same thread.
When a async task is first awaited, the task is put on a work queue. As soon as possible, the task scheduler grabs that task from the queue and assigns it to one of the many available threads.
For more information, see this overview of the structure of the TPL: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx.
If you need a context that flows with the thread, look at using something like the logical call context or CallContext.LogicalSetData / LogicalGetData.
But the behavior you're seeing is correct, and as mentioned has nothing to do with whether or not an exception is thrown. You'll see different thread ids at various points of an asynchronous task's scheduling, execution, and completion.

Marshalling data back to UI Thread from within Async Action

I've got an ICommand that needs to set data to a property on the UI Thread.
public override async void Execute(object parameter)
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
}
Now I want to wrap my call in an event logger (I originally wanted to do AOP and decorate the method with a logging attribute, but I couldn't figure it out in a PCL). So I moved onto wrapping my call like this.
public override void Execute(object parameter)
{
EventLogger.LogEvent(this,
EventLogEntryType.Command,
EventLogErrorSeverity.Warning,
Errors.GetServiceAreaCommand_ErrorMessage,
async () =>
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
});
}
Here's the LogEvent method.
public static void LogEvent(object sender,
EventLogEntryType entryType,
EventLogErrorSeverity eventLogErrorSeverity,
string friendlyErrorMessage,
Action action)
{
var name = sender.GetType().Name.SplitCamelCase();
var startEntry = new EventLogEntry(entryType);
LogEvent(string.Format("Start: {0}", name), startEntry);
try
{
action.Invoke();
}
catch (Exception ex)
{
var exEntry = new EventLogEntry(EventLogEntryType.Error, friendlyErrorMessage, false, ex)
{
ErrorSeverity = eventLogErrorSeverity
};
LogEvent(string.Format("Error: {0}", name), exEntry);
if (eventLogErrorSeverity == EventLogErrorSeverity.Critical)
{
throw;
}
}
var endEntry = new EventLogEntry(entryType);
LogEvent(string.Format("Finish: {0}", name), endEntry);
}
The problem is that it appears as though I'm STILL setting the property on a background thread instead of the Main thread (IllegalStateException in Android).
What is the cleanest way to set the data as is being done in the first example, while still wrapping the Action in a logging method?
I also had success creating a base class for ICommand, but it A) changed the method signatures for CanExecute and Execute, and B) it also (obviously) doesn't extend it's capabilities beyond Commands.
I'm looking for a clean way to log methods (BeforeExecute, AfterExecute, OnError) no matter what they do.
As an aside, the ideal logging mechanism would be to use an Interceptor, but I'm just not strong enough in my C# chops to implement it.
[Log(EventLogEntryType.Command, EventLogErrorSeverity.Warning, "Some Friendly Message")]
public override async void Execute(object parameter)
{
var vm = (MyVm)parameter;
var data = await _myDataService.GetData();
vm.MyData = data; // must be set on UI Thread due to binding.
}
If you have (caveat below) access to the Activity object in your code then you can probably do;
Activity.RunOnUiThread(() => {
//Execute my code on UIThread here
});
But it's an if, because I note you're using a PCL, or have referenced using one, so I suspect that a shared library is not going to know anything about the Activity (unless you pass that too). Very much depends on your app structure and where this code is, but within the main Xamarin.Android project where your views are the above should work

Async or parallel function

I am working on an application (ASP.NET MVC5) which saves a pile of data to the database in one go. The method which saves the data takes time to do it and I do not want to block user interface.
Here I have created a test program which will sleep for 10 sec and I do not want to return any result from this program.
public Task SaveFunc()
{
Thread.Sleep(10000);
return null;
}
public void ShowFunction()
{
SaveFunc();
retrun "Your request is under process";
}
Now, how do I call SaveFunc in such a way that I do not have to wait for the result.
You should use the async method Task.Delay since Thead.Sleep is synchronous and blocks the current context. You also need to return a Task from the method instead of null and await the Task to wait until it ends. In the mean time, your program can run as is:
public Task SaveFunc()
{
return Task.Delay(10000);
}
public async void ShowFunction()
{
await SaveFunc().ConfigureAwait(false);
}
This answer asumes you are using ASP.NET MVC - if this is not the case please update your question:
Since .NET 4.5.2 you can do the following:
public ActionResult ShowFunction()
{
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
// Some long-running job
});
return Content("Your request is under process");
}
If you are still on an old .NET version you can do something like:
public ActionResult ShowFunction()
{
ThreadPool.QueueUserWorkItem(c =>
{
// Some long-running job
});
return Content("Your request is under process");
}
but the execution of ThreadPool.QueueUserWorkItem can be canceled by an AppDomain-recycle so you need to take care of such scenarios. Since .NET 4.0 you can also use Task.Factory.StartNew(() => { /*...*/ }); and get a Task to work with.

Decent pattern for testable async operations?

I had trouble finding a simple, flexible pattern to allow me to write code in my ViewModels that in runtime would run asynchronously but during test-time run synchronously. This is what I came up with - does anyone have any suggestions? Is this a good path to go down? Are there better existing patterns out there?
LongRunningCall definition:
public class LongRunningCall
{
public Action ExecuteAction { get; set; }
public Action PostExecuteAction { get; set; }
public LongRunningCall(Action executeAction = null, Action postExecuteAction = null)
{
ExecuteAction = executeAction;
PostExecuteAction = postExecuteAction;
}
public void Execute(Action<Exception> onError)
{
try
{
ExecuteAction();
PostExecuteAction();
}
catch (Exception ex)
{
if (onError == null)
throw;
onError(ex);
}
}
public void ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
{
var executeTask = Task.Factory.StartNew(ExecuteAction);
var postExecuteTask = executeTask.ContinueWith((t) =>
{
if (t.Exception != null)
throw t.Exception;
PostExecuteAction();
}, scheduler);
if (onError != null)
postExecuteTask.ContinueWith((t) => { onError(t.Exception); });
}
}
Usage:
var continueCall = new LongRunningCall(continueCommand_Execute, continueCommand_PostExecute);
if (svc.IsAsyncRequired)
continueCall.ExecuteAsync(TaskScheduler.FromCurrentSynchronizationContext(), continueCommand_Error);
else
continueCall.Execute(continueCommand_Error);
The only real pre-requisite is that you need to know at runtime if you're supposed to use async/sync. When I run my unit tests I send in a mock that tells my code to run synchronously, when the application actually runs IsAsyncRequired defaults to true;
Feedback?
I would prefer to encapsulate the decision on whether to execute code synchronously or asynchronously in a separate class that can be abstracted behind an interface such as this:
public interface ITaskExecuter
{
void ScheduleTask(
Action executeAction,
Action postExecuteAction,
Action<Exception> onException);
}
An instance of a class implementing ITaskExecuter can be injected where required.
You can inject different instances for testing versus production scenarios.
Usage becomes:
taskExecuter.ScheduleTask(
continueCommand_Execute,
continueCommand_PostExecute,
continueCommand_Error);
with no separate code paths in the calling class for test versus production.
You have the option of writing tests that:
just check the correct actions are passed to the task executer, or
configuring the task executer to execute the action synchronously and
test for the desired result, or
do both.
I did something very simmilar at my current job, but can't get to the code to copy/paste it right now...
Basically what I did was to create an IWorker interface, with a DoWork(Func<>) method.
Then I created 2 derived classes, one 'AsyncWorker' and one 'SyncWorker'. The SyncWorker just executes the passed in Func (synchronously), and the 'AsyncWorker' is a wrapper around a BackgroundWorker that sends the passed in Func off to the BackgroundWorker to be processed asynchronously.
Then, I changed my ViewModel to have an IWorker passed in. This moves the dependency resolution out of the ViewModel, so you can use a Dep. Inj. utility (I use Unity and Constructor injection).
Since I use Unity, in my unit test configuration, I then map IWorker to SyncWorker, and in production I map IWorker to AsyncWorker.
Hope that makes sense... I know it'd be easier if I had the code on hand...
Consider changing ExecuteAsync so that it will return a Task:
public Task ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
So in production code, I would just call it as is:
longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
But in unit tests, I would wait for the task to actually finish:
var task = longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
task.Wait();

Categories

Resources