I am trying to generalize paging calls for an API. If I have a filter passed from the web page, I want to be able to return a range of items based off from the page number and page size. This is pretty simple to do if the OrderBy uses a set parameter or possibly using an ugly switch statement for every single call.
Currently I am trying to extend IQueryable using expressions and reflection (of which I am not experienced in) I found a couple examples to start from and it works if I use a property that is a simple type in the root class (passing the string "FixValidatedCount"). I can not get it to work if I try to order by a nested class property like the Island.Name example below.
Is there any way to updated my expression to accept nested class / properties? Or is there a better way to do this?
[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
});
}
The IssueSummary is the return type of the query.
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; }
}
The extension method for 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);
}
Aucun commentaire:
Enregistrer un commentaire