LINQ创建排序表达式类的基础属性,EF Core支持

6yoyoihd  于 2024-01-03  发布在  其他
关注(0)|答案(1)|浏览(84)

当使用LINQ OrderBy按类类型的属性排序时,我希望集合按类类型的属性之一的值排序。如何控制这一点,同时能够通过EF Core转换为SQL查询?

public class FooDate
{
    public DateTime DateTime {get; set;}
    public bool UserSetValue {get; set;}
    // other properties
}
public class Bar
{
    public FooDate Date {get; set;}
    // other properties
}

字符串
当我按类类型的属性排序时:

List<Bar> sortedInstances = existingList.OrderBy(x => x.Date).ToList();


我希望集合按FooDate.DateTime的值排序。
更新:
我尝试实现IComparable,但它没有在EF Core中转换为SQL。

public int CompareTo(object obj)
{
    if (obj == null) return 1;

    FooDate other = obj as FooDate;
    if (other != null)
        return this.DateTime.CompareTo(other.DateTime);
    else
        throw new ArgumentException("Object is not a FooDate");
}

fdx2calv

fdx2calv1#

首先,创建一个接口,指示类型有一个表达式,可以使用该表达式进行比较:

public interface IComparableExpression<TObject>
{
    static abstract Expression<Func<TObject, object>> Comparer { get; }
}

字符串
并在你的类上实现它:

public class FooDate : IComparableExpression<FooDate>
{
    // other properties

    public static Expression<Func<FooDate, object>> Comparer => fooDate => fooDate.DateTime;
}


然后创建您自己的OrderBy版本,将键限制为拥有自己的比较器:

public static IQueryable<TSource> OrderByComparable<TSource, TKey>(this IQueryable<TSource> query, 
    Expression<Func<TSource, TKey>> selector)
    where TKey : IComparableExpression<TKey>
{
    return query.OrderBy(selector.Compose(TKey.Comparer));
}


当您使用它时,您可能还需要每个版本的ThenByDescending

public static IQueryable<TSource> OrderByDescendingComparable<TSource, TKey>(this IQueryable<TSource> query,
    Expression<Func<TSource, TKey>> selector)
    where TKey : IComparableExpression<TKey>
{
    return query.OrderByDescending(selector.Compose(TKey.Comparer));
}
public static IQueryable<TSource> ThenByComparable<TSource, TKey>(this IOrderedQueryable<TSource> query, 
    Expression<Func<TSource, TKey>> selector)
    where TKey : IComparableExpression<TKey>
{
    return query.ThenBy(selector.Compose(TKey.Comparer));
}
public static IQueryable<TSource> ThenByDescendingComparable<TSource, TKey>(this IOrderedQueryable<TSource> query,
    Expression<Func<TSource, TKey>> selector)
    where TKey : IComparableExpression<TKey>
{
    return query.ThenByDescending(selector.Compose(TKey.Comparer));
}


这依赖于以下方法来组合两个表达式:

public static Expression<Func<TSource, TResult>> Compose<TSource, TIntermediate, TResult>(
    this Expression<Func<TSource, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TSource));
    var intermediateValue = first.Body.ReplaceParameter(first.Parameters[0], param);
    var body = second.Body.ReplaceParameter(second.Parameters[0], intermediateValue);
    return Expression.Lambda<Func<TSource, TResult>>(body, param);
}
public static Expression ReplaceParameter(this Expression expression,
    ParameterExpression toReplace,
    Expression newExpression)
{
    return new ParameterReplaceVisitor(toReplace, newExpression)
        .Visit(expression);
}
public class ParameterReplaceVisitor : ExpressionVisitor
{
    private ParameterExpression from;
    private Expression to;
    public ParameterReplaceVisitor(ParameterExpression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == from ? to : base.Visit(node);
    }
}


现在你有了(大部分)原始代码的工具:

List<Bar> sortedInstances = existingList.OrderByComparable(x => x.Date).ToList();


在幕后,它将构建一些等价于:

List<Bar> sortedInstances = existingList.OrderBy(x => x.Date.DateTime).ToList();

相关问题