简体   繁体   中英

LINQ Query across complex parent/child/parent/child relationships

This is probably a snap for a LINQ pro, but this is beyond my basic capacity. We have six tables accessed through Entity Framework:

Stores { Storeid, Description }
ShoppingDays { ShoppingDayid, Date, Storeid }
Transactions { Transactionid, Amount, ShoppingDayid, PaintColorid }
PaintColors { PaintColorid }
DyeAllocations { DyeAllocationid, PaintColorid, Percent, DyeId }
Dyes { DyeId, Name }

The Stores, ShoppingDays, and Transactions tables are straightforward and don't need commenting. However, each transaction purchases a single color of paint. Each color of paint consists of a mix of color dye percentages that add up to 100%.

I'd like to sum up all of the dollars spent on each dye on each day at each store. Imagine store1 has two transactions on day 1. One transaction for $30 for a purchase of Purple Paint (40% Red, 40% Blue, 20% Black) and another for $20 of Pink Paint (20% Red, 80% White). The results would look like

Store1,1,Red,$16
Store1,1,Blue,$12
Store1,1,Black,$6
Store1,1,White,$16

Any help would be most appreciated. I'm not really even sure where to start. I did an inner join of all the tables and then put the data into an excel pivot table to extract the data. Obviously that's not correct.

I started with the following. It provides a table that shows each dye purchase for each transaction. I'd like to sum up those purchases for each store and shopping day, but I'm not sure how.

var dyeValues = (from store in db.stores
                           join sd in db.shoppingdays on store.storeId equals sd.storeId
                           join tr in db.transactions on sd.shoppingdayId equals tr.shoppingdayId
                           join pc in db.paintcolors on tr.paintcolorId equals pc.paintcolorId
                           join da in db.dyeallocations on pc.paintcolorId equals da.paintcolorId
                           where da.percent > 0.0m
                           select new
                           {
                               store.Description,
                               shoppingdayDate = sd.Date,
                               da.dye.Name,
                               da.percent,
                               Allocation = da.percent * tr.Amount
                           });

Here is the equivalent of the classical SQL way.

First a subquery that groups by {ShoppingDayId, DyeId} and calculates Sum(Percent * Amount) :

var dyeAllocations =
    from tr in db.Transactions
    join pc in db.PaintColors on tr.PaintColorId equals pc.PaintColorId
    join da in db.DyeAllocations on pc.PaintColorId equals da.PaintColorId
    where da.Percent > 0.0m
    group new { Allocation =  da.Percent * tr.Amount }
    by new { tr.ShoppingDayId, da.DyeId } into g
    select new { g.Key.ShoppingDayId, g.Key.DyeId, Allocation = g.Sum(e => e.Allocation) };

Then join to other tables to get the additional information needed:

var dyeValues =
    from da in dyeAllocations
    join dye in db.Dyes on da.DyeId equals dye.DyeId
    join sd in db.ShoppingDays on da.ShoppingDayId equals sd.ShoppingDayId
    join store in db.Stores on sd.StoreId equals store.StoreId
    select new
    {
        store.Description,
        sd.Date,
        dye.Name,
        da.Allocation
    };

The subquery could be embedded in the actual query, I've used a separate variable just for readability (it has no effect on the EF generated SQL query). Also you may need to update the field names / casing to match the actual models, but this should give you the idea.

I modified Ivan's answer into a single query. It's very slow, but it works!

var deyValues = from cs in account.stores
       join sd in db.shoppingdays on cs.storeId equals sd.storeId
       join tr in db.transactions on sd.shoppingdayId equals tr.shoppingdayId
       join pc in db.paintcolors on tr.paintcolorId equals pc.paintcolorId
       join da in db.dyeallocations on pc.paintcolorId equals da.paintcolorId
       where da.AfterTaxOptimalPortion > 0.0m
       group new { Allocation = da.Percent * tr.Amount }
       by new { AccountName = cs.Account.Name, ShoppingDate = sd.Date, DyeName = da.dye.Name } into g
       select new { g.Key.AccountName, g.Key.ShoppingDate, g.Key.DyeName, Total = g.Sum(el => el.Allocation) };

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