Search code examples
c#asp.net-mvclinqasp.net-identity

How to find specific item with Linq based on multiple conditions?


I have pretty simple query:

//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));

What i want to do is to find specific product in WatchedProducts list. Model of it looks like this:

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 have list of WatchedProducts.

My question is, why istead of getting WatchedProduct product i get an IQueryable<WatchedProduct> product?


Solution

  • It happens because you are using method Where(). Where() method filters your data based on your lambda-expression => u.ApplicationUserId == user.Id && u.ProductId == id and returns IQueryable<TSource> or IEnumerable<TSource>(See great explanation in answer by Reza Aghaei).

    If you want to get WaterProduct product then just get it through FirstOrDefault() method:

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

    You did not get any data as you did not materialized your query. It is called deferred execution. Deferred execution means that your linq code will not be executed in database until you need necessary data. So to materialize data or execute your query in database you should call methods like:

    foreach, toList(), First(), FirstOrDefault(), Single(), SingleOrDefault(), etc...