TheSpixxyQ
TheSpixxyQ

Reputation: 1035

EF Core - complex query

I have this DB design (ProductCategories is a join table):

image

I need to query sold products, filter them by date, group by product ID and find all categories for that product. Plus some other details irrelevant for this question.

I have everything working except finding those categories.

My current working LINQ query looks like this:

var articles = await _context.PreordersProducts
    .Where(...)
    .GroupBy(p => new { p.Product.Code, p.Product.Id, Sum = p.Product.ProductVariants.Sum(v => v.InitialQuantity) })
    .Select(p => 
    new Article(
        p.Key.Id,
        p.Key.Code,
        p.Key.Sum
    ))
    .ToListAsync();

it gives me this result:

{
    "id": 11111,
    "code": "123-AAA",
    "initialQuantity": 50,
}

and I need to add categories so the result will look like this:

{
    "id": 11111,
    "code": "123-AAA",
    "initialQuantity": 50,
    "categories": [
        {
            "id": 1,
            "name": "category 1"
        },
        {
            "id": 2,
            "name": "category 2"
        },
    ]
}

I had two ideas but neither of them can be translated to SQL.

One was adding Categories to GroupBy:

.GroupBy(p => new
{
    p.Product.Code,
    p.Product.Id,
    Sum = p.Product.ProductVariants.Sum(v => v.InitialQuantity),
    Categories = p.Product.ProductCategories.Select(c => c.Category)
})

Second was adding SelectMany to Select:

.Select(p =>
new
{
    p.Key.Id,
    p.Key.Code,
    p.Key.Sum,
    Categories = p.SelectMany(c => c.Product.ProductCategories.Select(cc => cc.Category))
})

I belive something like this would be possible in SQL, but this is far beyond my SQL knowledge.

So, could you please help me, how to write this in single query?

Classes:

public partial class Product
{
    public int Id { get; private set; }
    public string Name { get; private set; }
    public string Code { get; private set; }

    public virtual ICollection<PreorderProduct> PreorderProducts { get; private set; }
    public virtual ICollection<ProductVariant> ProductVariants { get; private set; }
    public virtual ICollection<ProductCategory> ProductCategories { get; private set; }
}

public partial class ProductVariant
{
    public int Id { get; private set; }
    public int ProductId { get; private set; }
    public string Code { get; private set; }
    public int InitialQuantity { get; private set; }

    public virtual Product Product { get; private set; }
    public virtual ICollection<PreorderProduct> PreorderProducts { get; private set; }
}

public partial class PreorderProduct
{
    public int Id { get; private set; }
    public int ProductId { get; private set; }
    public int ProductVariantId { get; private set; }

    public virtual Product Product { get; private set; }
    public virtual ProductVariant ProductVariant { get; private set; }
}

public partial class ProductCategory
{
    public int Id { get; private set; }
    public int ProductId { get; private set; }
    public int CategoryId { get; private set; }

    public virtual Product Product { get; private set; }
    public virtual Category Category { get; private set; }
}

public partial class Category
{
    public int Id { get; private set; }
    public string Name { get; private set; }

    public virtual ICollection<ProductCategory> ProductCategories { get; private set; }
}

Upvotes: 2

Views: 821

Answers (2)

Svyatoslav Danyliv
Svyatoslav Danyliv

Reputation: 27282

I hope I've got the idea

var filtered = _context.PreordersProducts
    .Where(...);

var grouped = 
   from p in filtered
   from v in p.Product.ProductVariants
   group v by new { p.Product.Code, p.Product.Id } into g
   select new 
   {
        g.Key.Id,
        g.Key.Code,
        initialQuantity = g.Sum(x => x.InitialQuantity)
   };

var query = 
  from g in grouped
  select new
  {
     g.Id,
     g.Code,
     g.initialQuantity,
     categories =_context.ProductCategories
        .Where(x => x.ProductId == g.Id)
        .Select(x => new { id = x.Category.Id, name = x.Category.Name })
        .ToList()
  } 
  
var result = await query.ToListAsync();

Upvotes: 3

jdweng
jdweng

Reputation: 34421

Use following :

          Product _context = new Product();
           var articles = new
           {
               id = _context.Id,
               code = _context.Code,
               intialQuantity = _context.ProductCategories.Count(),
               categories = _context.ProductCategories.Select(x => new { productId = x.Id, id = x.Category.Id, name = x.Category.Name }).ToList()
           };

Upvotes: 0

Related Questions