29

Take the following code using ASP.NET Core 2.1:

[HttpGet("/unresolved")]
public async Task<ActionResult<IEnumerable<UnresolvedIdentity>>> GetUnresolvedIdentities()
{
   var results = await _identities.GetUnresolvedIdentities().ConfigureAwait(false);
   return results.ToList();
}

I would have thought since GetUnresolvedIdentities() returns IEnumerable<UnresolvedIdentity> that I could just return

return await _identities.GetUnresolvedIdentities().ConfigureAwait(false);

Except I can't, as I get this error:

CS0029 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Data.Infrastructure.Models.UnresolvedIdentity>' to 'Microsoft.AspNetCore.Mvc.ActionResult<System.Collections.Generic.IEnumerable<Data.Infrastructure.Models.UnresolvedIdentity>>'

I need the .ToList(), which is annoying as it's 2 lines rather than 1.

Why can't ActionResult<T> figure out that GetUnresolvedIdentities() returns an IEnumerable<> and just return that?

The signature of GetUnresolvedIdentities is:

Task<IEnumerable<UnresolvedIdentity>> GetUnresolvedIdentities();
poke
  • 369,085
  • 72
  • 557
  • 602
Stuart
  • 3,949
  • 7
  • 29
  • 58
  • 1
    Because `IEnumerable` is different than `IEnumerable`. Either change `_identities.GetUnresolvedIdentities` return type or your controller return type. – Andrei Tătar Aug 08 '18 at 08:58
  • Why not `return await _identities.GetUnresolvedIdentities().ConfigureAwait(false).ToList();`, if the "two lines" is the only thing bothering you? – Corak Aug 08 '18 at 08:59
  • 3
    @Corak because you can't `task.ToList()`. `(await task).ToList()` would work though – Vladi Pavelka Aug 08 '18 at 09:00
  • @VladiPavelka - yes, of course, thank you. ^_^ – Corak Aug 08 '18 at 09:01
  • 4
    You can wrap it around in ActionTask with the Ok helper method: `return Ok(await _identities.GetUnresolvedIdentities());`. P.S. you shouldn't call `.ConfigureAwait(false)` inside the controllers, unless you're 100% certain about the consequences (even though its not that much of an issue these days as it was in legacy ASP.NET). Its advised to always use it in **general purpose libraries** where its unknown in which context the library will be used – Tseng Aug 08 '18 at 09:27
  • @Tseng, good catch on the COnfigureAwait(false). Thanks – Stuart Aug 08 '18 at 09:29
  • 4
    You actually don’t need to use `ConfigureAwait` anywhere with ASP.NET Core since it does not have a synchronization context. – poke Aug 08 '18 at 14:24

2 Answers2

36

Take this documentation from msdn: https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.1#actionresultt-type

C# doesn't support implicit cast operators on interfaces. Consequently, conversion of the interface to a concrete type is necessary to use ActionResult<T>.

V0ldek
  • 9,623
  • 1
  • 26
  • 57
5

You can resolve this in a relatively tidy way by using Ok(...)

[HttpGet]
public ActionResult<IEnumerable<MyDTOObject>> Get() => Ok(Repo.GetObjects());

[HttpGet]
public async Task<ActionResult<IEnumerable<MyDTOObject>>> GetAsync() => Ok(await Repo.GetObjectsAsync());

Which assuming that GetObjects() and GetObjectsAsync() return a IEnumerable<MyDTOObject> and Task<IEnumerable<MyDTOObject>> respectively - allows you to skip .ToList() or .ToListAsync().

PhonicUK
  • 13,486
  • 4
  • 43
  • 62