What if I don't have any async work to perform? How should I write my InvokeAsync
method?
Warning CS1998 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread
namespace ViewComponentSample.ViewComponents
{
public class PriorityListViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(
int maxPriority, bool isDone)
{
var items = new List<Item>{ ... };
return View(items);
}
}
}
โ Do not edit this section. It is required for docs.microsoft.com โ GitHub issue linking.
Possibly like this ...
public async Task<IViewComponentResult> InvokeAsync(int maxPriority, bool isDone)
{
var items = new List<Item>{ ... };
return await Task.Run(() => View(items));
}
... but that might :boom: or get my picture up in the Async Programmer's Hall of Shame! :smile: I haven't had to do that in a real app.
@spottedmahn Why not make a synchronous method instead? Something like this:
namespace ViewComponentSample.ViewComponents
{
public class PriorityListViewComponent : ViewComponent
{
public IViewComponentResult Invoke(
int maxPriority, bool isDone)
{
var items = new List<Item>{ ... };
return View(items);
}
}
}
@guardrex nope, don't do that ๐
@scottaddie IViewComponentHelper
method sig demands a Task<T>
and an InvokeAsync
name.
https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.iviewcomponenthelper
@pranavkm How can a synchronous ViewComponent
be called without IViewComponentHelper
or resorting to Task.Run
with an async ViewComponent
?
@guardrex Thanks for the link. Once we figure this out, let's add an example to the doc. Others are likely wondering the same thing.
How can a synchronous ViewComponent be called without IViewComponentHelper or resorting to Task.Run with an async ViewComponent?
Task.FromResult(someResult)
~One small additional update ... add Result
to the call to get the output that way ...~ (no ... don't do this)
@Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true }).Result
[EDIT] Invoke it with the same Razor syntax ... @await Component.InvokeAsync( ... )
(no .Result
).
In general, making a blocking call (.Result
or .Wait()
) may result in thread starvation and deadlock your server. I wouldn't recommend using it.
@spottedmahn Looks like we'll have to leave this in the realm of workaround; therefore, not documented. However, this issue is attached to the topic, so readers who scroll to the bottom will see it.
Sorry, I hadn't paid attention to the original issue: you can write a ViewComponent
with a synchronous Invoke
method:
C#
public class PriorityListViewComponent : ViewComponent
{
public IViewComponentResult Invoke(int maxPriority, bool isDone)
{
var items = new List<Item>{ ... };
return View(items);
}
}
should work
@pranavkm How is it called in Razor with @Component
(IViewComponentHelper
)? It seems to want a Task<T>
and a method named InvokeAsync
.
https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.iviewcomponenthelper
MVC handles invoking a synchronous ViewComponent.Invoke
method. The same way you can have sync and async actions or page handlers, and not have to worry about how it gets invoked.
It seems a bit strange to call the dev's method (with a synchronous method signature) using the @await Component.InvokeAsync( ... );
syntax, but it :tada: _Just Works_:tm: ๐ .
I'll get this documented within a few days. Thanks everyone!
Cross-ref the discussion for the invocation changes: https://github.com/aspnet/Mvc/issues/3973
It seems a bit strange to call the dev's method (with a synchronous method signature) using the @await Component.InvokeAsync( ... );
๐
Based upon the comments above I've got this working:
public Task<IViewComponentResult> InvokeAsync()
{
var model = new MyModel
{
Name = "Hello world"
};
//have to cast to IViewComponentResult otherwise I get a compile error:
//Error CS0029 Cannot implicitly convert type
//'System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.ViewComponents.ViewViewComponentResult>'
//to
//'System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IViewComponentResult>'
//ViewComponent Sync Test C:\Users\mdepouw\source\repos\Experiments\ViewComponent Sync Test\ViewComponent Sync Test\Views\Shared\Components\Poc\PocViewComponent.cs
var result = (IViewComponentResult)View(model);
return Task.FromResult(result);
}
//doesn't work
//InvalidOperationException: Method 'InvokeAsync' of view component
//'ViewComponentSyncTest.Views.Shared.Components.MyViewComponent.PocViewComponent'
//should be declared to return Task<T>
//public IViewComponentResult InvokeAsync()
//{
// var model = new MyModel
// {
// Name = "Hello world"
// };
// return View(model);
//}
Full source: PocViewComponent.cs
@await Component.InvokeAsync(nameof(PocViewComponent).Replace("ViewComponent", ""))
Demo site: https://viewcomponentsync.azurewebsites.net/
Thanks to everyone! ASP.NET Core team rocks ๐๐ ๐
cleaner version with Task.FromResult<T>(T result)
...
public Task<IViewComponentResult> InvokeAsync()
{
var model = new MyModel
{
Name = "Hello world"
};
var result = View(model);
return Task.FromResult<IViewComponentResult>(result);
}
Based upon the PR, I see I can do the following:
@await Component.InvokeAsync(nameof(PocSyncViewComponent).Replace("ViewComponent", ""))
public class PocSyncViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
var model = new MyModel
{
Name = "hello world sync"
};
return View(model);
}
}
Feels like magic ๐งโโ๏ธ in that I'm telling it to call InvokeAsync()
but the runtime calls Invoke()
... not sure I'm a fan of that...
Thanks again ๐
@pranavkm See the comment from @spottedmahn above. This seems like an area that could use some polishing. Is there already an open issue on this in the aspnet/Mvc repo or elsewhere?
This will be โจ auto-closed โจ by the PR.
Most helpful comment
@guardrex Thanks for the link. Once we figure this out, let's add an example to the doc. Others are likely wondering the same thing.