基于API,我可以有多个参数可以在order by
中使用。有一个函数可以创建一个dynamic order by
参数作为字符串。我想在.OrderBy
中使用它,但不知道如何做到这一点。
API调用:
{{url}}?keyword=singer&page=12&size=5&sortby=LastName&sortby=FirstName
代码:
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(sortParameters.ToOrderBy()) // "LastName ASC, FirstName ASC"
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
通过以下方式获取订单的扩展方法:
static class RepositoryExtensions
{
public static string ToOrderBy(this IEnumerable<SortParameter> parameters)
{
return string.Join(", ", parameters.Select(p => p.SortBy + (p.Descending ? " DESC" : " ASC")));
}
}
Output:
"LastName ASC, FirstName ASC"
接受动态LINQ的扩展方法:
public static class Extension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof(T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi); // Errors out here.
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
}
错误:
System.ArgumentNullException: Value cannot be null.
Parameter name: property
Scrren开枪:
这是第一次使用此复杂查询,因此不确定如何执行此操作。如果需要,我可以添加更多信息。
答案 0 :(得分:0)
看起来错误发生是因为#Model
class Object(models.Model):
owner = models.ForeignKey(User)
category = models.ForeignKey(Category, null=True, default=None)
name = models.CharField(max_length=255)
address = models.CharField(max_length=255, default='')
#Serializer
class ObjectSerializer(ModelSerializer):
is_folder = BooleanField(read_only=True)
class Meta:
model = Object
fields = ('id', 'category', 'is_folder', 'name', 'address')
#ViewSet
class ObjectsViewSet(BaseViewSet, RetrieveModelMixin, ListModelMixin, CreateModelMixin, UpdateModelMixin,
DestroyModelMixin):
queryset = Object.objects
serializer_class = ObjectSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, ObjectPermission)
def get_queryset(self):
queryset = super(ObjectsViewSet, self).get_queryset()
return queryset.filter(owner=self.request.user)
#Permission
class ObjectPermission(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in ['GET']:
return request.user.has_perm('view_object')
if request.method in ['POST']:
return request.user.has_perm('add_object')
if request.method in ['PUT', 'PATCH']:
return request.user.has_perm('change_object')
if request.method in ['DELETE']:
return request.user.has_perm('delete_object')
return False
def has_object_permission(self, request, view, obj):
if request.method in ['GET']:
return request.user.has_perm('view_object', obj)
if request.method in ['PUT', 'PATCH']:
return request.user.has_perm('change_object', obj)
if request.method in ['DELETE']:
return request.user.has_perm('delete_object', obj)
return False
为空。并且它是null,因为,我认为,代表pi
泛型的类没有名为T
的属性。我会尝试以下内容:
LastName ASC, FirstName ASC
希望这会让你朝着正确的方向前进。
答案 1 :(得分:-1)
经过一些试验和错误后,我能够找到答案。
测试如下:
.OrderBy("LastName ASC, FirstName ASC")
.OrderBy("LastName ASC")
.OrderBy("LastName ASC,FirstName DESC")
<强>的LINQ:强>
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(o => o.Equals(sortParameters.ToOrderBy()))
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
助手类:
public static class OrderByHelper
{
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string orderBy)
{
return enumerable.AsQueryable().OrderBy(orderBy).AsEnumerable();
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, string orderBy)
{
foreach (var orderByInfo in ParseOrderBy(orderBy))
{
collection = ApplyOrderBy(collection, orderByInfo);
}
return collection;
}
private static IQueryable<T> ApplyOrderBy<T>(IQueryable<T> collection, OrderByInfo orderByInfo)
{
var props = orderByInfo.PropertyName.Split('.');
var type = typeof (T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
string methodName;
if (!orderByInfo.Initial && collection is IOrderedQueryable<T>)
{
methodName = orderByInfo.Direction == SortDirection.Ascending ? "ThenBy" : "ThenByDescending";
}
else
{
methodName = orderByInfo.Direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
}
return (IOrderedQueryable<T>) typeof (Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof (T), type)
.Invoke(null, new object[] {collection, lambda});
}
private static IEnumerable<OrderByInfo> ParseOrderBy(string orderBy)
{
if (string.IsNullOrEmpty(orderBy))
{
yield break;
}
var items = orderBy.Split(',');
var initial = true;
foreach (var item in items)
{
var pair = item.Trim().Split(' ');
if (pair.Length > 2)
{
throw new ArgumentException(
$"Invalid OrderBy string '{item}'. Order By Format: Property, Property2 ASC, Property2 DESC");
}
var prop = pair[0].Trim();
if (string.IsNullOrEmpty(prop))
{
throw new ArgumentException(
"Invalid Property. Order By Format: Property, Property2 ASC, Property2 DESC");
}
var dir = SortDirection.Ascending;
if (pair.Length == 2)
{
dir = "desc".Equals(pair[1].Trim(), StringComparison.OrdinalIgnoreCase)
? SortDirection.Descending
: SortDirection.Ascending;
}
yield return new OrderByInfo {PropertyName = prop, Direction = dir, Initial = initial};
initial = false;
}
}
private class OrderByInfo
{
public string PropertyName { get; set; }
public SortDirection Direction { get; set; }
public bool Initial { get; set; }
}
private enum SortDirection
{
Ascending = 0,
Descending = 1
}
<强> Referances:强>
Dynamic LINQ OrderBy on IEnumerable<T>
http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html
答案 2 :(得分:-1)
如果我理解正确的问题。
Expression<Func<TEntity, TKey>> genericParameter = null;
genericParameter = x => x.foo;
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(genericParameter)