简体   繁体   中英

How to use null-coalescing operator with ActionResult ASP.NET Core 2.1

Can someone explain me why I'm getting an error on the null-coalescing on the following method:

private readonly Product[] products = new Product[];

[HttpGet("{id}")]
public ActionResult<Product> GetById(int id)
{
    var product = products.FirstOrDefault(p => p.Id == id);
    if (product == null)
        return NotFound(); // No errors here
    return product; // No errors here

    //I want to replace the above code with this single line
    return products.FirstOrDefault(p => p.Id == id) ?? NotFound(); // Getting an error here: Operator '??' cannot be applied to operands of type 'Product' and 'NotFoundResult'
}  

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
}

What I don't understand is why the first returns are working without needs of any cast while the seconde single line null-coalescing doesn't works!

I'm targeting ASP.NET Core 2.1


Edit: Thanks @Hasan and @dcastro for the explanations, but I don't recommend to use the null-coalescing here as the NotFound() will not return the correct error code after the cast!

return (ActionResult<Product>)products?.FirstOrDefault(p => p.Id == id) ?? NotFound();

An error occurs because it is impossible to cast types.

Try this:

[HttpGet("{id}")]
public ActionResult<Product> GetById(int id)
{
    var result = products?.FirstOrDefault(p => p.Id == id);
    return result != null ? new ActionResult<Product>(result) : NotFound();
}
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public ActionResult<Product> GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return product;
}

In the preceding code, a 404 status code is returned when the product doesn't exist in the database. If the product does exist, the corresponding Product object is returned. Before ASP.NET Core 2.1, the return product; line would have been return Ok(product);.

As you can see from above code and explanation from microsoft's related page , after .NET Core 2.1 you do not need to return the exact type in the controller ( ActionResult<T> ) like before. To use that feature, you need to add attributes to indicate possible response types such as [ProducesResponseType(200)] and so on.

In your case, what you need to do is basically adding appropiate response type attributes to your controller method like shown below (since you develop with .NET Core 2.1).

[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public ActionResult<Product> GetById(int id)

Edit:

The reason why you can not compile the program (use null-coalescing operator) is that the return types are not competible. In one case it returns product class, otherwise it returns ActionResult<T> . After updating the code as I suggested I suppose you will be able to use null-coalescing operator.

2. Edit (Answered here)

After digging the issue more deeply, I have found out that when using ternary if statements or null coalescing operator we need to explicitly specify what type we expect to be produced from that statement when multiple types possibly might be returned. As asked before here , compiler doesn't decide which type it returns without implicitly casting it. So casting return type to ActionResult solves the problem.

return (ActionResult<Product>) products.FirstOrDefault(p=>p.id ==id) ?? NotFound();

But it's better to add response type attributes as shown above.

The OP's question can be split in two: 1) Why does the suggested null-coalescing expression not compile, and 2) is there another succinct ("single-line") way to return the result in ASP.NET Core 2.1?

As indicated in the second edit of @Hasan's answer, the result type of the null-coalescing operator is resolved according to the operand types, not the target type. Consequently, the OP's example fails, because there is no implicit conversion between Product and NotFoundResult :

products.FirstOrDefault(p => p.Id == id) ?? NotFound();

One way to fix it, while keeping succinct syntax, has been mentioned in a comment by @Kirk Larkin:

products.FirstOrDefault(p => p.Id == id) ?? (ActionResult<Product>)NotFound();

Starting with C# 8.0, you can also use a switch expression :

products.FirstOrDefault(p => p.Id == id) switch { null => NotFound(), var p => p };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM