Как использовать оператор слияния нулей с ActionResult ASP.NET Core 2.1

Может кто-нибудь объяснить мне, почему я получаю сообщение об ошибке слияния на следующий метод:

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; }
}

Чего я не понимаю, так это того, почему первые возвраты работают без необходимости какого-либо приведения, в то время как вторая одинарная нуль-слияние не работает!

Я ориентируюсь на ASP.NET Core 2.1


Редактировать: Спасибо @Hasan и @dcastro за объяснения, но я не рекомендую использовать нуль-слияние здесь как NotFound() не вернет правильный код ошибки после приведения!

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

3 ответа

Решение
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public ActionResult<Product> GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return product;
}

В предыдущем коде возвращается код состояния 404, когда продукт отсутствует в базе данных. Если продукт существует, возвращается соответствующий объект Product. До ASP.NET Core 2.1 возвращаемый продукт; строка была бы возвращена Ok(продукт);.

Как вы можете видеть из приведенного выше кода и объяснения на соответствующей странице Microsoft, после.NET Core 2.1 вам не нужно возвращать точный тип в контроллере (ActionResult<T>) как раньше. Чтобы использовать эту функцию, вам нужно добавить атрибуты, чтобы указать возможные типы ответов, такие как [ProducesResponseType(200)] и так далее.

В вашем случае вам нужно просто добавить подходящие атрибуты типа ответа в ваш метод контроллера, как показано ниже (поскольку вы разрабатываете с.NET Core 2.1).

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

Редактировать:

Причина, по которой вы не можете скомпилировать программу (используйте оператор объединения нулей), заключается в том, что возвращаемые типы не являются конкурентными. В одном случае он возвращает класс продукта, в противном случае он возвращает ActionResult<T>, После обновления кода, как я и предлагал, я полагаю, вы сможете использовать оператор объединения нулей.

2. Редактировать (ответили здесь)

После более глубокого изучения проблемы я обнаружил, что при использовании троичных операторов if или оператора объединения нулей нам необходимо явно указать, какой тип мы ожидаем получить из этого оператора, когда возможно будет возвращено несколько типов. Как уже было сказано выше, компилятор не решает, какой тип он возвращает, без явного его приведения. Таким образом, приведение типа возврата к ActionResult решает проблему.

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

Но лучше добавить атрибуты типа ответа, как показано выше.

Вопрос ОП можно разделить на два: 1) почему предлагаемое выражение объединения с нулевым значением не компилируется, и 2) есть ли другой краткий («однострочный») способ вернуть результат в ASP.NET Core 2.1?

Как указано во втором редактировании ответа @Hasan, тип результата оператора объединения с нулевым значением разрешается в соответствии с типами операндов, а не целевым типом. Следовательно, пример OP терпит неудачу, потому что нет неявного преобразования между Productа также NotFoundResult:

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

Один из способов исправить это, сохранив краткий синтаксис, был упомянут в комментарии @Kirk Larkin:

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

Начиная с C# 8.0, вы также можете использовать выражение переключения :

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

Произошла ошибка, потому что невозможно привести типы.

Попробуй это:

[HttpGet("{id}")]
public ActionResult<Product> GetById(int id)
{
    var result = products?.FirstOrDefault(p => p.Id == id);
    return result != null ? new ActionResult<Product>(result) : NotFound();
}
Другие вопросы по тегам