KaGaMi
KaGaMi
发布于 2025-08-06 / 14 阅读
0
0

实现较简单的OrderBy使用方式,适配使用起来表达式 比较复杂的ORM

前言

现在这边公司有着自己的ORM,对OrderBy函数的设计较为复杂

使用起来的方式是这样的:

queryOrder = query.OrderBy(x => new object[] { SqlHelper.Desc(() => x.Id) });

虽说“自定义性”比较高,但对于一个存在多个排序字段,并支持正倒序的Api,使用这个方式,代码可能就变成这个样子了


var isAsc = input.Asc == 0;

var queryOrder = query.OrderBy(x => new object[] { SqlOrder.Desc(() => x.ID) });

switch (input.OrderBy)

{

    case 1:

        queryOrder = isAsc

        ? query.OrderBy(x => new object[] { SqlOrder.Asc(() => x.ID) })

        : query.OrderBy(x => new object[] { SqlOrder.Desc(() => x.ID) }); break;

    case 2:

        queryOrder = isAsc

            ? query.OrderBy(x => new object[] { SqlOrder.Asc(() => x.ID) })

            : query.OrderBy(x => new object[] { SqlOrder.Desc(() => x.ID) }); break;

}

这让人多少感觉少了些优雅

比如看看List的Order

list.OrderBy(x => x.Id).ToList();

这样就很有C#的优雅味了

所以就按照这个形式,在原本的ORM上套一层方法,来达到似乎使用Linq的方式来实现这个排序函数

## 1.首先,先看下ORM接受的是什么格式的参数,表达式是怎样的

ORM.OrderBy函数传入的Expression<Func<T, object[]>>

组成表达式的内容{x => new [] {Convert(Desc(() => x.Id), Object)}}

这个看着就难受,一个obj的数组,里面还套一个函数,函数里面使用了外部的变量

使用之前Update的那种转换思路肯定不行的了,这里需要Expression<Func<T, object>>构造Expression<Func<T, object[]>>

按照这个思路,就比较清晰了,使用原始的Lambda构造方式去组装出想要的表达式到ORM即可

## 2.按照ORM的表达式,进行拆解,可以得到以下几个部分

() => x.Id

Convert(Desc(() => x.Id), Object)

new[] {}

x = > new[] {}

## 3.从里到外,先构造表达x变量

var parameterExpression = Expression.Parameter(typeof(T), "x");

## 4.构Desc函数

### 4.1获取并构造外部表达式传进来的参数

var expr2 = Expression.Property(parameterExpression, memberExpression.Member.Name);

结果x.Id

### 4.2构造Desc使用的表达式

var sqlHelperExpression = Expression.Lambda(expr2);

结果() => x.Id

## 5获取静态函数,并构造其泛型表达式

### 5.1获取函数信息 var orderMethod = typeof(SqlHelper).GetMethod("Desc");

### 5.2构造泛型函数信息 var orderGenericMethod = orderMethod.MakeGenericMethod(expr2.Type);

### 5.3构造调用表达式

var callExpr = Expression.Call(orderGenericMethod, sqlHelperExpression);

结果Desc(() => x.Id)

## 6.构object数组,并把Desc函数丢进参数中

### 6.1因为要的结果类型object[],类型与Desc不一样,使Expression.ConvertConvert()

var newArrayExpression = Expression.NewArrayInit(typeof(object), Expression.Convert(callExpr, typeof(object)));

结果new[] { Convert(Desc(() => x.Id), Object)}

### 6.2套上泛型变量,完成表达式组装

return Expression.Lambda<Func<T, object[]>>(newArrayExpression, parameterExpression);

## 完整代码:



public static Expression<Func<T, object[]>> OrderToOrmExp2<T>(Expression<Func<T, object>> ex, bool isAsc)

{

    if (ex.NodeType != ExpressionType.Lambda)

        throw new NotSupportedException($"Not Supported Order Expression NodeType,NodeType:{ex.NodeType}");

    if (ex.Body == null || ex.Body.NodeType != ExpressionType.Convert)

        throw new NotSupportedException($"Not Supported order expression formula or expression.body is Null,NodeType:{ex.NodeType}");

    var unaryExpression = (UnaryExpression)ex.Body;

    if (unaryExpression == null)

        throw new NotSupportedException($"Not Supported UnaryExpression formula,Expression:{ex}");

    var memberExpression = (MemberExpression)unaryExpression.Operand;

    if (memberExpression == null)

        throw new NotSupportedException($"Not Supported MemberExpression formula,Expression:{ex}");

    //表达式实体临时变量

    var parameterExpression = Expression.Parameter(typeof(T), "x");

    //SqlOrder的表达式内容

    var expr2 = Expression.Property(parameterExpression, memberExpression.Member.Name);

    var sqlHelperExpression = Expression.Lambda(expr2);

    //创建SqlOrder函数表达式

    var orderMethodName = isAsc ? nameof(SqlOrder.Asc) : nameof(SqlOrder.Desc);

    var orderMethod = typeof(SqlOrder).GetMethod(orderMethodName);

    var orderGenericMethod = orderMethod.MakeGenericMethod(expr2.Type);

    var callExpr = Expression.Call(orderGenericMethod, sqlHelperExpression);

    //构建ORM用的排序表达式

    var newArrayExpression = Expression.NewArrayInit(typeof(object), Expression.Convert(callExpr, typeof(object)));

    var funcCompiled = Expression.Lambda<Func<T, object[]>>(newArrayExpression, parameterExpression);

    return funcCompiled;

}

## 结果

虽然这个实现不难,但对于习惯了 CURD ,较少接触Expression组装的业务仔来说,确实不是很熟悉,特别Expression大量各种函数

虽说暂时只能靠直觉去使用,不过起码用过后学到了一种使用的直觉,不像初学那样一脸懵逼,连资料都不知道怎么去查

完成这个函数后,就可以在业务代码优雅地使用OrderBy了

var isAsc = input.Asc == 0;

var queryOrder = query.OrderByModel(x => x.ID, isAsc);

switch (input.OrderBy)

{

    case 1: queryOrder = query.OrderByModel(x => x.LabelID, isAsc); break;

    case 2: queryOrder = query.OrderByModel(x => x.AddTime, isAsc); break;

}


评论