C# 表达式按关键字段对通用查询进行排序
C# Expression to sort generic query by key field
我有一个通用方法,我想在其中按其关键字段对 IQueryable<T>
进行排序(假设只有一个是安全的)。因此:
void DoStuff<T>(...)
{
IQueryable<T> queryable = ... // given
PropertyInfo keyField = ... // given
var sortedQueryable = queryable.OrderBy(<some expression here>);
...
}
我如何定义一个 Expression
来 return keyField
属性 of T
以便它能工作?
我喜欢使用具有类型 T 的 Id 属性 的接口 IBaseEntity。这将使您的查询:
void DoStuff<IBaseEntity>(...)
{
IQueryable<IBaseEntity> queryable = ... // given
var sortedQueryable = queryable.OrderById(e=> e.Id); //extension method
...
}
扩展方法:
public static IQueryable<IBaseEntity<T>> OrderById<T>(this IQueryable<IBaseEntity<T>> query)
{
return query.OrderBy(e => e.Id);
}
每个实体都将实现 IBaseEntity 并具有类似
的东西
public partial class MyEntity : IBaseEntity<long>
{
[Required]
public override long Id
{
get { return base.Id;}
set { base.Id = value;}
}
}
然后在上下文中
modelBuilder
.Entity<MyEntity>()
.ToTable("DatabaseTable", "DatabaseSchema")
.HasKey(e => e.Id)
.Property(e => e.Id)
.HasColumnName("DatabasePrimaryKey")
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
注意:这需要在上下文和实体中进行一些设置。数据库可以有任何你想要的键,你将它们单独映射到上下文的 OnModelCreating 方法中的 属性 Id。
这并不太难,但是您需要通过反射调用 OrderBy
,因为您事先不知道关键字段的类型。所以给定你已经展示的代码,你会做这样的事情:
// Build up the property expression to pass into the OrderBy method
var parameterExp = Expression.Parameter(typeof(T), "x");
var propertyExp = Expression.Property(parameterExp, keyField);
var orderByExp = Expression.Lambda(propertyExp, parameterExp);
// Note here you can output "orderByExp.ToString()" which will give you this:
// x => x.NameOfProperty
// Now call the OrderBy via reflection, you can decide here if you want
// "OrderBy" or "OrderByDescending"
var orderByMethodGeneric = typeof(Queryable)
.GetMethods()
.Single(mi => mi.Name == "OrderBy" && mi.GetParameters().Length == 2);
var orderByMethod = orderByMethodGeneric.MakeGenericMethod(typeof(T), propertyExp.Type);
// Get the result
var sortedQueryable = (IQueryable<T>)orderByMethod
.Invoke(null, new object[] { queryable, orderByExp });
与 DavidG 回答中的想法相同,但方法不同
// build lambda expression (T t) => t.KeyField
var type = typeof(T);
var parameter = Expression.Parameter(type, "k");
var lambda = Expression.Lambda(Expression.Property(parameter, keyField), parameter);
// get source expression
var baseExpression = queryable.Expression;
// call to OrderBy
var orderByCall = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] {type, keyField.PropertyType},
baseExpression, lambda
);
// sorted result
var sorted = queryable.Provider.CreateQuery<T>(orderByCall);
我有一个通用方法,我想在其中按其关键字段对 IQueryable<T>
进行排序(假设只有一个是安全的)。因此:
void DoStuff<T>(...)
{
IQueryable<T> queryable = ... // given
PropertyInfo keyField = ... // given
var sortedQueryable = queryable.OrderBy(<some expression here>);
...
}
我如何定义一个 Expression
来 return keyField
属性 of T
以便它能工作?
我喜欢使用具有类型 T 的 Id 属性 的接口 IBaseEntity。这将使您的查询:
void DoStuff<IBaseEntity>(...)
{
IQueryable<IBaseEntity> queryable = ... // given
var sortedQueryable = queryable.OrderById(e=> e.Id); //extension method
...
}
扩展方法:
public static IQueryable<IBaseEntity<T>> OrderById<T>(this IQueryable<IBaseEntity<T>> query)
{
return query.OrderBy(e => e.Id);
}
每个实体都将实现 IBaseEntity 并具有类似
的东西public partial class MyEntity : IBaseEntity<long>
{
[Required]
public override long Id
{
get { return base.Id;}
set { base.Id = value;}
}
}
然后在上下文中
modelBuilder
.Entity<MyEntity>()
.ToTable("DatabaseTable", "DatabaseSchema")
.HasKey(e => e.Id)
.Property(e => e.Id)
.HasColumnName("DatabasePrimaryKey")
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
注意:这需要在上下文和实体中进行一些设置。数据库可以有任何你想要的键,你将它们单独映射到上下文的 OnModelCreating 方法中的 属性 Id。
这并不太难,但是您需要通过反射调用 OrderBy
,因为您事先不知道关键字段的类型。所以给定你已经展示的代码,你会做这样的事情:
// Build up the property expression to pass into the OrderBy method
var parameterExp = Expression.Parameter(typeof(T), "x");
var propertyExp = Expression.Property(parameterExp, keyField);
var orderByExp = Expression.Lambda(propertyExp, parameterExp);
// Note here you can output "orderByExp.ToString()" which will give you this:
// x => x.NameOfProperty
// Now call the OrderBy via reflection, you can decide here if you want
// "OrderBy" or "OrderByDescending"
var orderByMethodGeneric = typeof(Queryable)
.GetMethods()
.Single(mi => mi.Name == "OrderBy" && mi.GetParameters().Length == 2);
var orderByMethod = orderByMethodGeneric.MakeGenericMethod(typeof(T), propertyExp.Type);
// Get the result
var sortedQueryable = (IQueryable<T>)orderByMethod
.Invoke(null, new object[] { queryable, orderByExp });
与 DavidG 回答中的想法相同,但方法不同
// build lambda expression (T t) => t.KeyField
var type = typeof(T);
var parameter = Expression.Parameter(type, "k");
var lambda = Expression.Lambda(Expression.Property(parameter, keyField), parameter);
// get source expression
var baseExpression = queryable.Expression;
// call to OrderBy
var orderByCall = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] {type, keyField.PropertyType},
baseExpression, lambda
);
// sorted result
var sorted = queryable.Provider.CreateQuery<T>(orderByCall);