LINQ - SQL Script Changing based on JOIN Order

烂漫一生 提交于 2019-12-13 03:29:34

问题


This is just strange behavior to me.

LINQ transforming my statements creating bizarre execution plans. It's adding sub-queries when I don't ask it to and it's including or excluding joins in this sub-query based on the order of my joins, which leaves me scratching my head. At this point it's impossible for me to optimize this script based on the unpredictable nature of LINQ to Entities.

SETUP

//define global filter
Expression<Func<Contract_SeqExecution, bool>> globalFilter = r => r.ModifiedByFirstName.Contains("w");

...

//extend global filter
Expression<Func<Contract_SeqExecution, bool>> descendantFilter = x => x.client_id == 1 && x.project_status == true && x.parentId != null;
descendantFilter = descendantFilter.And(globalFilter);

//query
IQueryable<Contract_SeqExecution> geDescendantResults = c.queryGroups(this);

//execute
geDescendantResults.AsExpandable().Where(descendantFilter).Dump();

LINQ QUERY

public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
{
        var result = (from ge in context.group_execution
                        join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
                        join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
                        from modify_u in modify_uSub.DefaultIfEmpty()
                        join p in context.project on aseq.project_id equals p.id
                        join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
                        join es in context.execution_schedule on ge.schedule_id equals es.id
                        join exe_u in context.users on ge.executed_by_id equals exe_u.id
                        join create_u in context.users on aseq.created_by_id equals create_u.id
                        select new Contract_SeqExecution
                        {
                            client_id = p.client_id,
                            project_status = p.status,
                            ID = ge.id,
                            Name = aseq.name,
                            ModifiedByFirstName = modify_u.first_name,
                            ModifiedByLastName = modify_u.last_name,
                            FailedInd = asstatus.fail_alert_ind,
                            parentId = ge.parent_group_exec_id,
                            patriarchId = ge.patriarch_id
                        });
        return result;
}

If I reorder the joins in the above statement my execution plan changes and LINQ-to-entities decides I want a sub-query....sigh.

Seriously all I did was change the order of the joins in the images below. Drastically different execution plans which will later on hurt performance of the query. What gives? Am I missing something obvious?

Is it picking information up from the entity framework schema? Could it be missing FK definitions or indexes that are driving this crazy behavior? At this point I'm just shooting in the dark. Hopefully someone here can shed some light on this.


Below is the class Contract_SeqExecution. I'm currently running this in a LinqPad C# Program hitting my DAL dll, linq-to-entities.

public class Contract_SeqExecution
{
    public int ID { get; set; }   
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public int SeqID { get; set; }
    public int CaseGroupInd { get; set; }
    public string CaseGroupText { get; set; }
    public string ExecRatio { get; set; }
    public string ModifiedByFirstName { get; set; }
    public string ModifiedByLastName { get; set; }
    public string Project { get; set; }
    public string Uploaded { get; set; }
    public string UploadRatio { get; set; }
    public bool FailedInd { get; set; }
    public bool HoldInd { get; set; }
    public int? parentId { get; set; }
    public int? patriarchId { get; set; }
    public DateTime? SchedRunTime { get; set; }

    //other fields
    public string Machine {get;set;}
    public int is_accepting_changes {get;set;}
    public string holding_at_name {get;set;}
    public string RunStatus {get;set;}
    public string CaseGroupStatus {get; set;}
    public string TCStatusColor {get; set;}
    public string ExecutedBy {get;set;}
    public string CreatedBy {get;set;}
    public int? ScheduleID {get;set;}
    public bool SeqUploaded {get;set;}
    //end other fields

    public int? parent_group_exec_id { get; set; }
    public int client_id { get; set; }
    public bool project_status { get; set; }

    public IQueryable<Contract_SeqExecution> queryGroups(UserQuery context)
    {
            var result = (from ge in context.group_execution
                            join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
                            join es in context.execution_schedule on ge.schedule_id equals es.id
                            join exe_u in context.users on ge.executed_by_id equals exe_u.id
                            join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
                            join p in context.project on aseq.project_id equals p.id
                            join create_u in context.users on aseq.created_by_id equals create_u.id
                            join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
                            from modify_u in modify_uSub.DefaultIfEmpty()
                            select new Contract_SeqExecution
                            {
                                client_id = p.client_id,
                                project_status = p.status,
                                ID = ge.id,
                                Name = aseq.name,
                                ModifiedByFirstName = modify_u.first_name,
                                ModifiedByLastName = modify_u.last_name,
                                FailedInd = asstatus.fail_alert_ind,
                                parentId = ge.parent_group_exec_id,
                                patriarchId = ge.patriarch_id,
                            });
            return result;
    }
}

UPDATE

So I took Andrew's advice and looked into doing this the more "LINQ" way. I like to include FKs in my entities because I feel it's a more natural way to write SQL, habits die hard. I went ahead and rewrote my LINQ expression and the results are now consistent.

Example:

var result = (from ge in context.group_execution
                //join asstatus in context.automation_sequence_status on ge.run_status_id equals asstatus.id
                //join es in context.execution_schedule on ge.schedule_id equals es.id
                //join exe_u in context.users on ge.executed_by_id equals exe_u.id
                //join aseq in context.automation_sequences on ge.automation_sequence_id equals aseq.id
                //join p in context.project on aseq.project_id equals p.id
                //join create_u in context.users on aseq.created_by_id equals create_u.id
                //join modify_u in context.users on aseq.last_modified_by_id equals modify_u.id into modify_uSub
                //from modify_u in modify_uSub.DefaultIfEmpty()
                select new Contract_SeqExecution
                {
                    //client_id = p.client_id,
                    //project_status = p.status,
                    ID = ge.id,
                    Name = ge.automation_sequences.name,
                    ModifiedByFirstName = ge.automation_sequences.modified_by_user.first_name,
                    ModifiedByLastName = ge.automation_sequences.modified_by_user.last_name,
                    //FailedInd = asstatus.fail_alert_ind,
                    parentId = ge.parent_group_exec_id,
                    patriarchId = ge.patriarch_id,
                });
return result;

来源:https://stackoverflow.com/questions/48574140/linq-sql-script-changing-based-on-join-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!