简体   繁体   中英

How to increase performance with a foreign key in a large query with Entity Framework?

I work with Entity Framework. Here is examples of my SQL tables :

RPM_Data

RPM_ID  Year    Month   Budget_Code Code_IAM    Actual_Days Direction_ID    Cost
1       2014    1       03-G317     S00140802   0,03125     7               5,5
2       2014    2       01-G001     S00220105   0,01525     6               2,5
3       2014    3       01-G001     S00140802   0,05015     5               10

Direction

Id  Label
5   Direction_n1
6   Direction_n2
7   Direction_n3

I need to get the sum of each "Actual_Days" per Budget_Line, Code_IAM and Direction for all the year. So it looks like a list of objects like this :

BudgetCode : "03-G317" 
Code_IAM   : "S00140802" 
Direction  : "Direction_n3"
ActualJan  : 0.325
ActualFeb  : 1.25
ActualMar  : 1.325
ActualApr  : 0.75
ActualMay  : 3.25
ActualJun  : 2.325
ActualJul  : 4.25
ActualAug  : 1.125
ActualSep  : 0.22
ActualOct  : 0.325
ActualNov  : 2.325
ActualDec  : 4.325
Cost       : 12554.25

So I come up with the following query :

var directions = db.Direction.ToList();
List<Actual> query = (from r in db.RPM_Data
                    where r.Year == DateTime.UtcNow.Year && lines.Contains(r.Budget_Code)
                    group r by new { r.Budget_Code, r.Code_IAM, r.Direction_ID } into grp
                    select
                        new Actual
                            {
                                BudgetLine = grp.Key.Budget_Code,
                                CodeIam = grp.Key.Code_IAM,
                                Direction = db.Direction.Where(d => d.Id == grp.Key.Direction_ID)
                                                        .Select(d => d.Label)
                                                        .FirstOrDefault(),
                                ActualJan = grp.Sum(x => x.Month == 1 ? x.Actual_Days : 0),
                                ActualFeb = grp.Sum(x => x.Month == 2 ? x.Actual_Days : 0),
                                ActualMar = grp.Sum(x => x.Month == 3 ? x.Actual_Days : 0),
                                ActualApr = grp.Sum(x => x.Month == 4 ? x.Actual_Days : 0),
                                ActualMay = grp.Sum(x => x.Month == 5 ? x.Actual_Days : 0),
                                ActualJun = grp.Sum(x => x.Month == 6 ? x.Actual_Days : 0),
                                ActualJul = grp.Sum(x => x.Month == 7 ? x.Actual_Days : 0),
                                ActualAug = grp.Sum(x => x.Month == 8 ? x.Actual_Days : 0),
                                ActualSep = grp.Sum(x => x.Month == 9 ? x.Actual_Days : 0),
                                ActualOct = grp.Sum(x => x.Month == 10 ? x.Actual_Days : 0),
                                ActualNov = grp.Sum(x => x.Month == 11 ? x.Actual_Days : 0),
                                ActualDec = grp.Sum(x => x.Month == 12 ? x.Actual_Days : 0),
                                Cost = grp.Sum(x => x.Cost)
                            }
                    ).ToList();

But the sub-query with db.Direction destroys my performance...

So I tried to "store" all my db.Direction first and get the Id from it later.

var directions = db.Direction.ToList();
// And then in my query 
// ...
Direction = directions.Where(d => d.Id == grp.Key.Direction_ID)
                      .Select(d => d.Label)
                       .FirstOrDefault(),
// ...

But I get the following error :

Unable to create a constant value of type 'OPManager.DataAccess.Direction'. Only primitive types or enumeration types are supported in this context.

I also tried without .ToList() and get an error to.

Can you tell me what is the proper way to handle this foreign key in my query?

How can I have the best performance in order to get what I want?

Try to set the Direction property later after ToList , but store the Direction_ID temporary.

public class Action
{
   [NotMapped]
   public int Direction_ID { get; set; }
}

The query.

List<Actual> query = (from r in db.RPM_Data
                    where r.Year == DateTime.UtcNow.Year && lines.Contains(r.Budget_Code)
                    group r by new { r.Budget_Code, r.Code_IAM, r.Direction_ID } into grp
                    select
                        new Actual
                        {
                           BudgetLine = grp.Key.Budget_Code,
                           CodeIam = grp.Key.Code_IAM,
                           //Direction = ... -> leave this property unset.                       
                           Direction_ID = grp.Key.Direction_ID, // but set the id.        
                        }
                    ).ToList();

Then set the direction.

foreach(var a in query)
{
    a.Direction = directions.Where(d => d.Id == a.Direction_ID)
                     .Select(d => d.Label)
                     .FirstOrDefault();
    // Hides the direction id if necessary.
    a.Direction_ID = 0;
}

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