C# 组合多个表达式以创建Linq选择器表达式
我正在尝试动态构建Linq查询的select语句。 我有这样一个函数:C# 组合多个表达式以创建Linq选择器表达式,c#,linq,expression,C#,Linq,Expression,我正在尝试动态构建Linq查询的select语句。 我有这样一个函数: public Task<List<T>> RunQuery<T>( IQueryable<T> query, FieldSelector<T> fields, int pageNumber, int pageSize) { var skip = (pageNumber-1) * pageSize; var query =
public Task<List<T>> RunQuery<T>(
IQueryable<T> query,
FieldSelector<T> fields,
int pageNumber, int pageSize)
{
var skip = (pageNumber-1) * pageSize;
var query = query.Skip(skip).Take(pageSize);
var selector = fields.GetSelector();
return query.Select(selector).ToListAsync();
}
公共任务运行查询(
可查询的查询,
字段选择器字段,
int pageNumber,int pageSize)
{
var skip=(页码-1)*页面大小;
var query=query.Skip(Skip).Take(pageSize);
var selector=fields.GetSelector();
返回query.Select(selector.toListSync();
}
这是FieldSelector类:(I我的代码I每个字段都有额外的属性)
公共类字段选择器
{
私有列表表达式;
公共字段选择器()
{
表达式=新列表();
}
公共空添加(表达式表达式表达式)
{
添加(expr);
}
公共表达式GetSelector()
{
//构建一个表达式,如e=>new{e.Name,e.Street}
}
}
如何实现GetSelector函数?可能吗?(不会变得太复杂)。我想这样使用它:
var fields = new FieldSelector<Employee>();
fields.Add(e => e.Name);
fields.Add(e => e.Street);
RunQuery<Employee>(query, fields, 1, 100);
var fields=new FieldSelector();
fields.Add(e=>e.Name);
添加(e=>e.Street);
RunQuery(查询,字段,1100);
您需要生成一个自定义类型,它是如何为匿名类型
生成编译器的,因为您无法生成具有动态属性计数的真正的匿名类型。生成此类型后,您可以轻松设置传递给FieldSelector
的表达式的赋值,并将其组合为自定义类型
public class FieldSelector<T>
{
private List<LambdaExpression> expressions;
public FieldSelector()
{
expressions = new List<LambdaExpression>();
}
public void Add(Expression<Func<T, object>> expr)
{
expressions.Add(expr);
}
public Expression<Func<T, object>> GetSelector()
{
// We will create a new type in runtime that looks like a AnonymousType
var str = $"<>f__AnonymousType0`{expressions.Count}";
// Create type builder
var assemblyName = Assembly.GetExecutingAssembly().GetName();
var modelBuilder = AppDomain.CurrentDomain
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect)
.DefineDynamicModule("module");
var typeBuilder = modelBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class);
var types = new Type[expressions.Count];
var names = new List<string>[expressions.Count];
for (int i = 0; i < expressions.Count; i++)
{
// Retrive passed properties
var unExpr = expressions[i].Body as UnaryExpression;
var exp = unExpr == null ? expressions[i].Body as MemberExpression : unExpr.Operand as MemberExpression;
types[i] = exp.Type;
// Retrive a nested properties
names[i] = GetAllNestedMembersName(exp);
}
// Defined generic parameters for custom type
var genericParams = typeBuilder.DefineGenericParameters(types.Select((_, i) => $"PropType{i}").ToArray());
for (int i = 0; i < types.Length; i++)
{
typeBuilder.DefineField($"{string.Join("_", names[i])}", genericParams[i], FieldAttributes.Public);
}
// Create generic type by passed properties
var type = typeBuilder.CreateType();
var genericType = type.MakeGenericType(types);
ParameterExpression parameter = Expression.Parameter(typeof(T), "MyItem");
// Create nested properties
var assignments = genericType.GetFields().Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i])));
return Expression.Lambda<Func<T, object>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter);
}
private Expression GetAllNestedMembers(Expression parameter, List<string> properties)
{
Expression expression = parameter;
for (int i = 0; i < properties.Count; ++i)
{
expression = Expression.Property(expression, properties[i]);
}
return expression;
}
private List<string> GetAllNestedMembersName(Expression arg)
{
var result = new List<string>();
var expression = arg as MemberExpression;
while (expression != null && expression.NodeType != ExpressionType.Parameter)
{
result.Insert(0, expression.Member.Name);
expression = expression.Expression as MemberExpression;
}
return result;
}
}
不幸的是,cusObj将是编译类型的对象
,如果不将其标记为动态
,则无法检索其属性
顺便说一下,您的公共任务RunQuery
不会被编译,因为field.GetSelector()
返回Func
,当您调用returnreturn query.Select(field.GetSelector()).ToListAsync()时,您将得到一个任务
希望,这会有所帮助。您需要生成一个自定义类型,它是如何为匿名类型
生成编译器的,因为您无法生成具有动态属性计数的真正的匿名类型。生成此类型后,您可以轻松设置传递给FieldSelector
的表达式的赋值,并将其组合为自定义类型
public class FieldSelector<T>
{
private List<LambdaExpression> expressions;
public FieldSelector()
{
expressions = new List<LambdaExpression>();
}
public void Add(Expression<Func<T, object>> expr)
{
expressions.Add(expr);
}
public Expression<Func<T, object>> GetSelector()
{
// We will create a new type in runtime that looks like a AnonymousType
var str = $"<>f__AnonymousType0`{expressions.Count}";
// Create type builder
var assemblyName = Assembly.GetExecutingAssembly().GetName();
var modelBuilder = AppDomain.CurrentDomain
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect)
.DefineDynamicModule("module");
var typeBuilder = modelBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class);
var types = new Type[expressions.Count];
var names = new List<string>[expressions.Count];
for (int i = 0; i < expressions.Count; i++)
{
// Retrive passed properties
var unExpr = expressions[i].Body as UnaryExpression;
var exp = unExpr == null ? expressions[i].Body as MemberExpression : unExpr.Operand as MemberExpression;
types[i] = exp.Type;
// Retrive a nested properties
names[i] = GetAllNestedMembersName(exp);
}
// Defined generic parameters for custom type
var genericParams = typeBuilder.DefineGenericParameters(types.Select((_, i) => $"PropType{i}").ToArray());
for (int i = 0; i < types.Length; i++)
{
typeBuilder.DefineField($"{string.Join("_", names[i])}", genericParams[i], FieldAttributes.Public);
}
// Create generic type by passed properties
var type = typeBuilder.CreateType();
var genericType = type.MakeGenericType(types);
ParameterExpression parameter = Expression.Parameter(typeof(T), "MyItem");
// Create nested properties
var assignments = genericType.GetFields().Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i])));
return Expression.Lambda<Func<T, object>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter);
}
private Expression GetAllNestedMembers(Expression parameter, List<string> properties)
{
Expression expression = parameter;
for (int i = 0; i < properties.Count; ++i)
{
expression = Expression.Property(expression, properties[i]);
}
return expression;
}
private List<string> GetAllNestedMembersName(Expression arg)
{
var result = new List<string>();
var expression = arg as MemberExpression;
while (expression != null && expression.NodeType != ExpressionType.Parameter)
{
result.Insert(0, expression.Member.Name);
expression = expression.Expression as MemberExpression;
}
return result;
}
}
不幸的是,cusObj将是编译类型的对象
,如果不将其标记为动态
,则无法检索其属性
顺便说一下,您的公共任务RunQuery
不会被编译,因为field.GetSelector()
返回Func
,当您调用returnreturn query.Select(field.GetSelector()).ToListAsync()时,您将得到一个任务
希望对您有所帮助。谢谢!这个很好用!我只需要使用这个,因为我使用的是dotnet核心:AssemblyBuilder.DefinedDynamicAssemblyThank!这个很好用!我之所以使用它,是因为我使用的是dotnet核心:AssemblyBuilder.DefinedDynamicAssembly
private class TestClass
{
public string Arg2 { get; set; }
public TestClass Nested { get; set; }
public int Id { get; set; }
}
var field = new FieldSelector<TestClass>();
field.Add(e => e.Arg2);
field.Add(e => e.Id);
field.Add(e => e.Nested.Id);
dynamic cusObj = field.GetSelector().Compile()(new TestClass { Arg2 = "asd", Id = 6, Nested = new TestClass { Id = 79 } });
Console.WriteLine(cusObj.Arg2);
Console.WriteLine(cusObj.Id);
Console.WriteLine(cusObj.Nested_Id);