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();
[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.