Gopal Thakur
Gopal Thakur

Reputation: 17

Generic ActionResule<T> in .NET Core doesnot respect the type T

In the below example, GET method has return type of

ActionResult<SomeAltResult>

but the return statement returns OKResult with an IEnumerable of WeatherForecast and still it is compiling successfully.

I am trying to understand, if I can return anything, what is the point of having generic ActionResult<T>? I can use ActionResult or IActionResult.

If ActionResult<T> does not inherit any class or interface chain of OkObjectResult then how is polymorphism working here?

namespace WebApplication1.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]{
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm","Balmy", "Hot", "Sweltering", "Scorching"
    };
    
    [HttpGet(Name = "GetWeatherForecast")]
    public ActionResult<SomeAltResult> Get()
    {
        var x = Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
    
        return Ok(x);
    }
}

public class SomeAltResult
{
    public string Name { get; set; }
}

Upvotes: 1

Views: 143

Answers (1)

Mark Seemann
Mark Seemann

Reputation: 233347

If ActionResult<T> does not inherit any class or interface chain of OkObjectResult then how is polymorphism working here?

The ActionResult<T> class defines an implicit conversion from any ActionResult to ActionResult<T>, which explains why it compiles.

Code like this also compiles:

ActionResult<SomeAltResult> actionResult = Ok(x);

That conversion most likely makes use of the constructor overload that takes any ActionResult as input. Notice that it's not constrained on the generic type parameter in any way. Thus, you can use any ActionResult, including OkObjectResult.

I am trying to understand, if I can return anything, what is the point of having generic ActionResult<T>? I can use ActionResult or IActionResult.

It is, indeed, an odd API design. Consider this variation of the OP code:

ActionResult<SomeAltResult> actionResult = Ok(x);
SomeAltResult? value = actionResult.Value; // null
return Ok(value);

This also compiles, but the web resource now returns an empty result. This happens because actionResult.Value returns null since it has no value of the type T.

I can't explain that API design choice. Such silent failures indicate to me an API design flaw. Code like that shouldn't be allowed to compile without warnings.

The reason that the OP works despite of all this is that the only thing that the ASP.NET framework cares about is whether a Controller Action returns an IActionResult, and the ExecuteResultAsync method doesn't care about generics. It only coverts the IActionResult object to an HTTP response, and at the boundaries, static types don't exist.

Upvotes: 1

Related Questions