出于教育目的,我正在重写作为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
依次按Rank
,LastName
,然后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中是否有必要(不要太过离题,但是我知道这是否应该是一个单独的问题)?在这种情况下,最佳做法是什么?