实体框架核心3动态顺序不起作用

时间:2019-12-16 16:24:01

标签: entity-framework entity-framework-core

public class Branch
 {
    [Sortable(OrderBy = "BranchId")]
    public long BranchId { get; set; }
    public string Name { get; set; }
    public string Code { get; set; }
    public string Type { get; set; }
}

这是我的Model类,我还创建了一个自定义属性

public class SortableAttribute : Attribute
    {
        public string OrderBy { get; set; }
    }

现在我用orderby降序创建分页,但是此代码不起作用

public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source,
            GeneralPagingRequest pagingRequest, int indexFrom = 0,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (indexFrom > pagingRequest.PageNumber)
            {
                throw new ArgumentException(
                    $"indexFrom: {indexFrom} > pageNumber: {pagingRequest.PageNumber}, must indexFrom <= pageNumber");
            }

            var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
            var items = source.Skip(((pagingRequest.PageNumber - 1) - indexFrom) * pagingRequest.PageSize)
                .Take(pagingRequest.PageSize);
            var props = typeof(T).GetProperties();
            PropertyInfo orderByProperty;
            orderByProperty =
                    props.FirstOrDefault(x=>x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);


            if (pagingRequest.OrderBy == "desc")
            {
                items = items.OrderBy(x => orderByProperty.GetValue(x));
            }
            else
            {
                items = items.OrderBy(x => orderByProperty.GetValue(x));
            }

            var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
            var pagedList = new PagedList<T>
            {
                PageNumber = pagingRequest.PageNumber,
                PageSize = pagingRequest.PageSize,
                IndexFrom = indexFrom,
                TotalCount = count,
                Items = result,
                TotalPages = (int) Math.Ceiling(count / (double) pagingRequest.PageSize)
            };

            return pagedList;
} 

但结果变量创建异常

1 个答案:

答案 0 :(得分:0)

.OrderBy()需要一个委托,该委托将告诉它 HOW (选择),而不是键值本身。因此,您正在这里查看一些元编程。

自然地,您将看到构建动态的LINQ表达式树,该树将为您获取属性:

// your code up above
PropertyInfo orderByProperty = props.FirstOrDefault(x => x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);

var p = Expression.Parameter(typeof(T), "x"); // you define your delegate parameter here
var accessor = Expression.Property(p, orderByProperty.GetMethod); // this basically becomes your `x => x.BranchId` construct
var predicate = Expression.Lambda(accessor, p).Compile(); // here's our problem: as we don't know resulting type at compile time we can't go `Expression.Lambda<T, long>(accessor, p)` here

if (pagingRequest.OrderBy == "desc")
{
    items = items.OrderByDescending(x => predicate(x)); // passing a Delegate here will not work as OrderBy requires Func<T, TKey>
}
else
{
    items = items.OrderBy(x => predicate(x)); // passing a Delegate here will not work as OrderBy requires Func<T, TKey>
}

var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
// your code down below

上面的代码存在问题-您不预先知道TKey。因此,我们将不得不更深入,并动态地构建整个items.OrderBy(x => x.BranchId)表达式。信念的最大飞跃将是OrderBy是一种扩展方法,它实际上驻留在IQueryable类型上。在获得通用方法参考之后,您将需要在知道属性类型时构建特定的委托类型。这样您的方法就变成这样:

public static class ExtToPagedListAsync
{
    private static readonly MethodInfo OrderByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderBy" && method.GetParameters().Length == 2); // you need your method reference, might as well find it once

    private static readonly MethodInfo OrderByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2); // you need your method reference, might as well find it once

    public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> source, GeneralPagingRequest pagingRequest, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken))
    {

        if (indexFrom > pagingRequest.PageNumber)
        {
            throw new ArgumentException(
                $"indexFrom: {indexFrom} > pageNumber: {pagingRequest.PageNumber}, must indexFrom <= pageNumber");
        }

        var count = await source.CountAsync(cancellationToken).ConfigureAwait(false);
        var items = source.Skip(((pagingRequest.PageNumber - 1) - indexFrom) * pagingRequest.PageSize)
            .Take(pagingRequest.PageSize);
        var props = typeof(T).GetProperties();
        PropertyInfo orderByProperty = props.FirstOrDefault(x => x.GetCustomAttributes(typeof(SortableAttribute), true).Length != 0);

        var p = Expression.Parameter(typeof(T), "x");
        var accessor = Expression.Property(p, orderByProperty.GetMethod);
        var predicate = Expression.Lambda(accessor, p); // notice, we're not yet compiling the predicate. we still want an Expression here

        // grab the correct method depending on your condition
        MethodInfo genericMethod = (pagingRequest.OrderBy == "desc") ? OrderByDescendingMethod.MakeGenericMethod(typeof(T), orderByProperty.PropertyType)
                                                                    :OrderByMethod.MakeGenericMethod(typeof(T), orderByProperty.PropertyType);

        object ret = genericMethod.Invoke(null, new object[] { items, predicate });
        items = (IQueryable<T>)ret; // finally cast it back to Queryable with your known T

        var result = await items.ToListAsync(cancellationToken).ConfigureAwait(false);
        var pagedList = new PagedList<T>
        {
            PageNumber = pagingRequest.PageNumber,
            PageSize = pagingRequest.PageSize,

            IndexFrom = indexFrom,
            TotalCount = count,
            Items = result,
            TotalPages = (int)Math.Ceiling(count / (double)pagingRequest.PageSize)
        };

        return pagedList;
    }
}

我必须透露我确实从this answer here得到了一些启发,因此请检查一下以供进一步阅读。