LINQ嵌套选择是最有效的查询吗?

时间:2018-09-12 17:38:18

标签: c# linq

出于教育目的,我正在重写作为Web API创建的旧的MVC Web应用程序,并试图更深入地了解LINQ。我正在尝试找出在代码优先EF中使用LINQ从多个表中查询和排序数据的最有效方法。

我有一个轮班表,其中包含计划的轮班信息。每个班次可以分配多名员工(Shift --<ShiftEmployees>-- Employee,也可以在不同位置分配各种活动,着装不同,着装时间也不同(Shift --<ShiftActivities>-- Activity/Location/ Uniform)。

以下基本查询将产生此结果:

var shift = _context.Shifts
            .Include(s => s.Platoon)
            .Include(s => s.ShiftActivities).ThenInclude(sa => sa.Activity)
            .Include(s => s.ShiftActivities).ThenInclude(sa => sa.Uniform)
            .Include(s => s.ShiftActivities).ThenInclude(sa => sa.Location)
            .Include(s => s.ShiftEmployees).ThenInclude(se =>  se.Employee).ThenInclude(e => e.Rank)
            .FirstOrDefault(s => s.ShiftID == id);

{
"shiftID": 1,
"shiftDate": "2018-09-03T00:00:00",
"information": null,
"platoon": {
    "platoonID": 2,
    "platoonName": "B Platoon"
},
"shiftEmployees": [
    {
        "shiftEmployeeID": 1,
        "userID": null,
        "pinStatus": 3,
        "employee": {
            "employeeID": 1,
            "serialNumber": "34567",
            "lastName": "Test2",
            "firstName": "Employee",
            "notifyEmail": false,
            "receiptEmail": false,
            "notifySMS": false,
            "receiptSMS": false,
            "rank": {
                "rankID": 1,
                "rankName": "Paygrade III",
                "shortName": "P-III",
                "accessLevel": 1
            },
            "platoon": {
                "platoonID": 2,
                "platoonName": "B Platoon"
            }
        }
    },
    {
        "shiftEmployeeID": 2,
        "userID": null,
        "pinStatus": 3,
        "employee": {
            "employeeID": 2,
            "serialNumber": "12345",
            "lastName": "Test",
            "firstName": "Employee",
           .....Other fields omitted for brevity....
            "rank": {
                .....Other fields omitted for brevity....
            },
            "platoon": {
                .....Other fields omitted for brevity....
            }
        }
    }
],
"shiftActivities": [
    {
        "shiftActivityID": 2,
        "startTime": "12:50:05",
        "activity": {
            "activityID": 2,
            "activityName": "1st Muster"
        },
        "location": {
            "locationID": 2,
            "locationCode": 99,
            "locationName": "Location 2",
            "locationAddress": null,
            "locationCity": null,
            "locationZip": null,
            "latitude": null,
            "longitude": null
        },
        "uniform": {
            "uniformID": 1,
            "uniformName": "A Uniform",
            "uniformDescription": null
        }
    },
    {
        "shiftActivityID": 3,
        "startTime": "15:28:25",
        "activity": {
            .....Other fields omitted for brevity....
        },
        "location": {
           .....Other fields omitted for brevity....
        },
        "uniform": {
            .....Other fields omitted for brevity....
        }
    },
    {
        Additional `shiftActivity` omitted for brevity 
    }
]
}

这将返回所需的数据,但是,我想根据开始时间对ShiftActivities进行排序,并且还要对ShiftEmployees依次按RankLastName,然后SerialNumber。因此,我想到了以下查询:

var shift = _context.Shifts
            .Include(s => s.Platoon)
            .Select(s => new
            {
                shift = s,
                shiftEmployees = s.ShiftEmployees.Select(se => new
                {
                    _shiftEmployee = se,
                    employee = se.Employee,
                    rank = se.Employee.Rank
                }).OrderBy(se => se.employee.LastName)
                  .ThenBy(se =>  se.employee.SerialNumber),
                shiftActivities = s.ShiftActivities.Select(sa => new
                {
                    _shiftActivity = sa,
                    activity = sa.Activity,
                    location = sa.Location,
                    uniform = sa.Uniform,
                }).OrderBy(sa => sa._shiftActivity.StartTime)
            })
            .OrderBy(s => s.shift.Platoon)
            .FirstOrDefault(s => s.shift.ShiftID == id);

{
"shift": {
    "shiftID": 1,
    "shiftDate": "2018-09-03T00:00:00",
    "information": null,
    "platoon": {
        "platoonID": 2,
        "platoonName": "B Platoon"
    },
    "shiftEmployees": null,
    "shiftActivities": null
},
"shiftEmployees": [
    {
        "_shiftEmployee": {
            "shiftEmployeeID": 1,
            "userID": null,
            "pinStatus": 3,
            "employee": null
        },
        "employee": {
           "employeeID": 1,
            "serialNumber": "34567",
            "lastName": "Test2",
            "firstName": "Employee",
            "notifyEmail": false,
            "receiptEmail": false,
            "notifySMS": false,
            "receiptSMS": false,
            "rank": null,
            "platoon": null
        },
        "rank": {
                "rankID": 1,
                "rankName": "Paygrade III",
                "shortName": "P-III",
                "accessLevel": 1
        }
    },
    {
        "_shiftEmployee": {
           .....Other fields omitted for brevity....
        },
        "employee": {
            .....Other fields omitted for brevity....
        },
        "rank": {
            .....Other fields omitted for brevity....
        }
    }
],
"shiftActivities": [
    {
        "_shiftActivity": {
            "shiftActivityID": 2,
            "startTime": "12:50:05",
            "activity": null,
            "location": null,
            "uniform": null
        },
        "activity": {
            "activityID": 2,
            "activityName": "1st Muster"
        },
        "location": {
            "locationID": 2,
            "locationCode": 99,
            "locationName": "Location 2",
            "locationAddress": null,
            "locationCity": null,
            "locationZip": null,
            "latitude": null,
            "longitude": null
        }
        "uniform": {
            "uniformID": 1,
            "uniformName": "A Uniform",
            "uniformDescription": null
        }            
    },
    {
        "_shiftActivity": {
           .....Other fields omitted for brevity....
        },
        "activity": {
            .....Other fields omitted for brevity....
        },
        "location": {
           .....Other fields omitted for brevity....
        },      
        "uniform": {
           .....Other fields omitted for brevity....
        }           
    },
    {
      Additional `shiftActivity` omitted for brevity           
    }
]
}

在我看来,第一个查询似乎更井井有条,更易于阅读,尽管它的排序方式没有必要。根据PostMan的说法,第二个查询产生的数据量增加了200个字节(可以忽略该网站的少量访问量),返回时间在几毫秒内。

第二个查询是否是实现所述目标的最有效方法?我想我想要的结果是像第二个查询一样对数据进行排序,同时保持第一个查询的结构和可读性。

此外,由于两种方法都提取了比用户看到的更多的数据(例如,notifyEmail/SMS上的Employee字段和排信息,accessLevel中的Rank等)是否建议使用ViewModel(或数据模型,因为这是API)并且仅填充我需要的字段? ViewModels在MVC中可以很好地工作,但是它们在Web API中是否有必要(不要太过离题,但是我知道这是否应该是一个单独的问题)?在这种情况下,最佳做法是什么?

0 个答案:

没有答案