为最近项目的日志 table 排序一对多关系

Sorting a 1-to-many relationship for a log table for the most recent item

所以我有一个 table,设备,其中包含资产。有一个 table 以一对多的关系连接到这个,记录每次清点、由谁以及清点的位置。它历史地存储这些,以便他们可以回过头来找出谁在什么时候做了什么。我的问题是,当我尝试按 InventoryDate 排序时,我在 table 上显示最后一个库存日期并希望按服务器端排序。

我使用 jquery datatables 来显示数据,并尝试根据对排序箭头的点击对 asc/desc 进行排序。错误是:

'DbSortClause 表达式必须具有顺序可比的类型。 参数名称:key'

            int totalDBRecords = eqp.Count();
            int filteredDBRecords = totalDBRecords;
            int pageSize = dtp.length != -1 ? dtp.length : totalDBRecords;
            int skip = dtp.start;

            string search = dtp.search.value;

            string dir = dtp.order[0].dir.ToUpper();
            //string orderFilter;

            IQueryable<eqp> orderFilter;

            IQueryable<eqp> pageData = eqp;

            //Column Sorting
            switch (dtp.columns[dtp.order[0].column].name)
            {
                case "c1":
                    orderFilter = dir == "ASC" ? pageData.OrderBy(d => string.IsNullOrEmpty(d.c1)).ThenBy(d => d.c1) : pageData.OrderByDescending(d => d.c1);
                    break;
                case "c2":
                    orderFilter = dir == "ASC" ? pageData.OrderBy(d => string.IsNullOrEmpty(d.c2)).ThenBy(d => d.c2) : pageData.OrderByDescending(d => d.c2);
                    break;
                case "c3":
                    orderFilter = dir == "ASC" ? pageData.OrderBy(d => string.IsNullOrEmpty(d.c3)).ThenBy(d => d.c3) : pageData.OrderByDescending(d => d.c3);
                    break;
                case "c4":
                    orderFilter = dir == "ASC" ? pageData.OrderBy(d => string.IsNullOrEmpty(d.c4)).ThenBy(d => d.c4) : pageData.OrderByDescending(d => d.c4);
                    break;
                case "c5":
                    orderFilter = dir == "ASC" ? pageData.OrderBy(d => string.IsNullOrEmpty(d.c5)).ThenBy(d => d.c5) : pageData.OrderByDescending(d => d.c5);
                    break;
                default:
                    orderFilter = pageData.OrderByDescending(d => d.c3).ThenByDescending(n => n.c0);
                    break;
            }

            pageData = orderFilter;
            ...
            ...
            filteredDBRecords = pageData.Count();

            pageData = pageData.Skip(skip).Take(pageSize);

            ...
            return new JsonResult() { Data = model, MaxJsonLength = Int32.MaxValue };```

首先,声明如下:

IQueryable<Equipment> orderFilter;

IQueryable<Equipment> pageData = equipment;

pageData = orderFilter;

...实际上什么都不做,它们只是让代码更难阅读。设置对彼此的引用不会像 copy/preserve 这些引用指向的数据那样做任何事情。这通常还会导致代码“忘记”它实际使用的引用的错误,并且链式操作最终会相互覆盖,从而导致意外结果。

这段代码也没什么意义:

pageData.OrderBy(d => string.IsNullOrEmpty(d.Serial)).ThenBy(d => d.Serial) : 
pageData.OrderByDescending(d => d.Serial)

您试图告诉 EF 在实体上订购 string.IsNullOrEmpty() 而不是 属性。事实上,您的实体 中的字符串可能 为 null 并且可能需要特别考虑到订购中,这与您所写的不同。

通常对于条件排序,您会使用类似的东西:

switch (dtp.columns[dtp.order[0].column].name)
{
    case "Serial":
        employees = dir == "ASC" 
            ? employees.OrderBy(d => d.Serial) 
            : employees.OrderByDescending(d => d.Serial);
        break;
    // ...
}

如果你想处理#null 序列值首先或最后等等,那么:

? employees.OrderBy(d => d.Serial ?? "00000") 
: employees.OrderByDescending(d => d.Serial ?? "00000");

...用合适的默认值替换“00000”。

下一个细节可能是您希望能够根据多个排序标准进行排序,而您的示例仅考虑第一个排序标准。这有点棘手,因为您需要在第一个条件下 OrderBy 然后在所有后续条件下 ThenBy 。有一个稍微 hacky work-around 使这个简单:

employees = employees.OrderBy(e => 0); // Does nothing, but allows us to `ThenBy` our multiple conditions.

foreach ( var condition in dtp.order)
{
    var isAscending = condition.dir.ToUpper() == "ASC";
    switch (dtp.columns[condition.column].name)
    {
        case "Serial":
            employees = isAscending 
                ? employees.ThenBy(d => d.Serial) 
               : employees.ThenByDescending(d => d.Serial);
        break;
        // ...
    }
}

这样您就可以传递多个条件并将排序附加到 IQueryable

所有 linq OrderBy 方法以动态方式重用都很烦人。

首先,我建议将查询投影到您显示的结果中。特别是为了在尝试对结果进行排序之前计算 d.GovEquipmentInvLogs.Max(x => x.InventoryDate) 一次。

然后我会引入一个扩展方法来根据参数调用.[Order/Then]By[Descending]方法;

public IOrderedQueryable<T> Order<T, V>(this IQueryable<T> query, Expression<Func<T, V>> key, bool then, bool desc)
    => (desc)
        ? (then ? ((IOrderedQueryable<T>)query).ThenByDescending(key) : query.OrderByDescending(key))
        : (then ? ((IOrderedQueryable<T>)query).ThenBy(key) : query.OrderBy(key));

现在您可以大大整理您的 switch 语句。尽管您也可以消除它,但如果您希望求助于动态创建表达式树来识别每个 属性。但这是一个单独的 SO 问题,已经有了答案。