Как найти конкретный предмет с Linq на основе нескольких условий? - PullRequest
0 голосов
/ 27 декабря 2018

У меня довольно простой запрос:

//user from UserManager from default AccountController from .net Identity
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

var product = await Task.Run(() 
    => db.WatchedProducts.Where(u => u.ApplicationUserId == user.Id && u.ProductId == id));

Что я хочу сделать, это найти конкретный product в WatchedProducts списке.Модель выглядит следующим образом:

public class WatchedProduct
{
    [Key]
    public int Id { get; set; }

    [ForeignKey("ApplicationUser")]
    public string ApplicationUserId { get; set; }
    public virtual ApplicationUser ApplicationUser { get; set; }

    [ForeignKey("Product")]
    public int ProductId { get; set; }
    public virtual Product Product { get; set; }
}

ApplicationUser имеют список WatchedProducts.

Мой вопрос: почему вместо WatchedProduct product я получаю IQueryable<WatchedProduct> product?

Ответы [ 3 ]

0 голосов
/ 27 декабря 2018

Вы должны использовать SingleOrDefault

Из того, что я могу сказать, ваш результат должен быть уникальным, так как вы используете идентификатор продукта, который будет уникальным

var product = await Task.Run(() 
    => db.SingleOrDefault(u => u.ApplicationUserId == user.Id && u.ProductId == id));

Это вернет один элемент или ноль, еслиничего не было найдено.Осторожно, он выдаст исключение, если будет найдено более одного элемента, чего не должно быть, так как идентификатор продукта, скорее всего, будет уникальным, и если он найдет больше, то вы узнаете, что вы запутались в своей базе данных, что у вас есть несколько продуктов стот же идентификатор

Если допустимо, чтобы этот запрос мог иметь более одной записи в качестве результата, тогда используйте FirstOrDefault вместо SingleOrDefault, но тогда логика будет не совсем правильной, поскольку она не делаетсмысл иметь запрос, который должен вернуть один или ни одного, чтобы вернуть первый в списке.

0 голосов
/ 27 декабря 2018

Это потому, что Where метод расширения возвращает IEnumerable<TSource>.В случае установки db возвращается IQueryable<TSource>, что составляет IEnumerable<TSource>.

. В вашем случае, поскольку вы используете шаблон асинхронного / ожидающего выполнения, вы можете использовать FirstOrDefaultAsync, чтобы получить один элемент в результате:

var p = await db.WatchedProducts.FirstOrDefaultAsync(u => u.ApplicationUserId == user.Id &&
                                                          u.ProductId == id)
0 голосов
/ 27 декабря 2018

Это происходит потому, что вы используете метод Where().Where() метод фильтрует ваши данные на основе вашего лямбда-выражения => u.ApplicationUserId == user.Id && u.ProductId == id и возвращает IQueryable<TSource> или IEnumerable<TSource> (см. Большое объяснение в ответе Резы Агаи).

Если вы хотите получить продукт WaterProduct, тогдапросто получите его через FirstOrDefault() метод:

var product = await Task.Run(() 
    => db.WatchedProducts.Where(u => u.ApplicationUserId == user.Id && u.ProductId == id)
       .FirstOrDefault());

Вы не получили никаких данных, так как не материализовали свой запрос.Это называется отложенным исполнением.Отложенное выполнение означает, что ваш код linq не будет выполняться в базе данных, пока вам не понадобятся необходимые данные.Таким образом, чтобы материализовать данные или выполнить ваш запрос в базе данных, вы должны вызывать методы, такие как:

...