简体   繁体   中英

How to GroupBy data from many tables in dotnet core Entity Framework

I'm building some marketplace web app, let's say something like e-bay. Typical scenario is:

User makes offer which consists of one or more items and those items are of certain type.After that other users are bidding on that offer.

Here is simplified diagram.

图表

On SQL Fiddle ( here ) you can see both CREATE TABLE and INSERT INTO statements

Sample data: There are two offers. On one offer (Id 1) which consists of one item which is type of "watch". There is another offer, (Id 2), which has one item which is of type "headphone". On both offers there are bids. On watch, there are two bis; one bid with 100 dollars and another with 120. On headphones, there are bids with 50 and 80 dollars.

What I want to achieve is to have average bid per type. In this sample, that means i want to get 110 as average bid for watch and 65 as average bid for headphone. To achieve that using T-SQL, I would write query like this:

SELECT t.name,
       avg(amount)
FROM bid b
LEFT JOIN offer o ON b.OfferId = o.id
LEFT JOIN offeritem oi ON o.id = oi.OfferId
LEFT JOIN itemType t ON oi.itemtypeid = t.Id
GROUP BY t.name

So, my question is - how to achieve that in dotnet core 3.0 EntityFramework

Using GroupBy, like this:

_context.Bids
    .Include(b => b.Offer)
        .ThenInclude(o => o.OfferItems)
            .ThenInclude(os => os.ItemType)
    .GroupBy(b => b.Offer.OfferItems.First().ItemType.Name);

gives exception:

Client side GroupBy is not supported.

. When I try with projection, like this:

_context.Bids
    .Include(b => b.Offer)
        .ThenInclude(o => o.OfferItems)
            .ThenInclude(os => os.ItemType)
    .GroupBy(b => b.Offer.OfferItems.First().ItemType.Name)
    .Select(g => new
    {
        Key = g,
        Value = g.Average(b => b.Amount)
    });

i get exception again.

Processing of the LINQ.... failed. This may indicate either a bug or a limitation in EF Core.

EDIT:

This approach

_context.Bids
    .Include(b => b.Offer)
        .ThenInclude(o => o.OfferItems)
            .ThenInclude(os => os.ItemType)
    .GroupBy(b => new { b.Offer.OfferItems.First().ItemType.Name}, b => b.Amount)
    .Select(g => new
    {
        Key = g.Key.Code,
        Value = g.Average()
    });

also threw an exception, but this time:

Cannot use an aggregate or a subquery in an expression used for the group by list of a GROUP BY clause.

...

So, is there a way to group that data (get simple Average) or should I make another query and iterate throught collection and make calculation myself? That would lower performance for sure (I was hoping I can do server grouping, but as you can see, i got into mentioned issues). Any ideas? Thanks in advance.

In your case it is hard to hide subquery from grouping

You can try it in such way

var joined =
    context.Bid
        .SelectMany(x =>
            x.Offer.OfferItem
                .Select(y => new
                {
                    Amount = x.Amount,
                    Name = y.ItemType.Name
                })
                .Take(1));

var grouped = from i in joined
    group i by i.Name into groups
    select new
    {
        Key = groups.Key,
        Amount = groups.Average(x => x.Amount)
    };

it gives me a query

SELECT [t].[Name] AS [Key], AVG([t].[Amount]) AS [Amount]
  FROM [Bid] AS [b]
  INNER JOIN [Offer] AS [o] ON [b].[OfferId] = [o].[Id]
  CROSS APPLY (
      SELECT TOP(1) [b].[Amount], [i].[Name], [o0].[Id], [i].[Id] AS [Id0], [o0].[OfferId]
      FROM [OfferItem] AS [o0]
      INNER JOIN [ItemType] AS [i] ON [o0].[ItemTypeId] = [i].[Id]
      WHERE [o].[Id] = [o0].[OfferId]
  ) AS [t]
  GROUP BY [t].[Name]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM