【问题标题】:Paging API calls using Linq to Sql OrderBy with string parameter and reflection使用带有字符串参数和反射的 Linq to Sql OrderBy 进行分页 API 调用
【发布时间】:2016-02-26 05:33:37
【问题描述】:

我正在尝试概括 API 的分页调用。如果我有一个从网页传递的过滤器,我希望能够根据页码和页面大小返回一系列项目。如果 OrderBy 使用 set 参数或者可能对每个调用使用丑陋的 switch 语句,这很容易做到。

目前我正在尝试使用表达式和反射(我没有经验)扩展 IQueryable 我找到了几个示例开始,如果我在根类中使用简单类型的属性(传递字符串“FixValidatedCount”)。如果我尝试通过嵌套类属性(如下面的 Island.Name 示例)进行排序,我无法使其工作。

有没有办法更新我的表达式以接受嵌套类/属性?或者有更好的方法吗?

    [Route("api/issues/paged")]
    [HttpPost]
    public HttpResponseMessage GetIssuesPage(EntityPageFilter filter)
    {
        //THE BELOW COMMENTED OUT OBJECT IS THE PARAMETER 
        //var filter = new EntityPageFilter
        //{
        //    PageSize = 5,
        //    PageNumber = 1,
        //    OrderBy = "Island.Name",
        //    OrderByAscending = true
        //};

        var query = _issueService.GetIssues()
            .OrderByField(filter.OrderBy, filter.OrderByAscending)
            //.OrderBy(i => i.Island.Name)  THIS WORKS, BUT HOW DO I DO THIS WITH A STRING
            .Skip(filter.Skip).Take(filter.PageSize);

        return Request.CreateResponse(HttpStatusCode.OK, new PagedEntity<IssueSummary>
        {
            PageNumber = filter.PageNumber,
            PageSize = filter.PageSize,
            ItemCount = _issueService.GetIssues().Count(),
            Data = query
        });
    }

IssueSummary 是查询的返回类型。

public class Issue
{
    public int Id { get; set; }
    public Island Island { get; set; }
    public Type Type { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime? FixedAt { get; set; }
}

public class IssueSummary: Issue
{
    public int FixCount { get; set; }
    public int FixValidatedCount { get; set; }
}

public class Island
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

IQueryable 的扩展方法

    public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string sortField, bool ascending)
    {
        var properties = sortField.Split('.').ToList();

        var xType = typeof(T);

        for (var i = 0; i < properties.Count - 1; i++)
        {
            xType = xType.GetProperty(properties[i]).PropertyType;
        }

        var param = Expression.Parameter(xType, String.Empty);
        var prop = Expression.Property(param, properties.Last());

        var exp = Expression.Lambda(prop, param);
        var method = ascending ? "OrderBy" : "OrderByDescending";
        //var types = new[] { q.ElementType, exp.Body.Type };
        var types = new[] { xType, exp.Body.Type };
        var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
        return q.Provider.CreateQuery<T>(mce);
    }

【问题讨论】:

    标签: c# linq reflection linq-to-sql linq-expressions


    【解决方案1】:

    获取具有嵌套支持的属性/字段访问器表达式的最简单方法如下:

    var root = Expression.Parameter(typeof(T), "x");
    var member = sortField.Split('.').Aggregate((Expression)root, Expression.PropertyOrField);
    var selector = Expression.Lambda(member, root);
    

    这里是完整的扩展方法:

    public static IQueryable<T> OrderByField<T>(this IQueryable<T> source, string sortField, bool ascending)
    {
        var root = Expression.Parameter(typeof(T), "x");
        var member = sortField.Split('.').Aggregate((Expression)root, Expression.PropertyOrField);
        var selector = Expression.Lambda(member, root);
        var method = ascending ? "OrderBy" : "OrderByDescending";
        var types = new[] { typeof(T), member.Type };
        var mce = Expression.Call(typeof(Queryable), method, types,
            source.Expression, Expression.Quote(selector));
        return source.Provider.CreateQuery<T>(mce);
    }
    

    【讨论】:

    • 效果很好,非常感谢 Ivan,正是我想要的。不过,我没有足够的声誉来投票,所以如果其他人使用它,请这样做。
    猜你喜欢
    • 2011-08-26
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多