在 C# 中将表达式的一部分定义为变量
Define part of an Expression as a variable in c#
我有以下代码:
public class MyClass<T>
{
Expression<Func<T,bool>> Criteria {get; set;}
}
public class Customer
{
//..
public string Name {get; set;}
}
并按如下方式使用它:
var c = new MyClass<Customer>();
c.Criteria = x.Name.StartWith("SomeTexts");
有没有办法定义这样的东西:
? p = x=>x.Customer.Name;
var c = new MyClass<Customer>();
c.Criteria = p => p.StartWith("SomeTexts");
我使用 Expression<Func<T,bool>>
将其用作 linq to entities
查询中的 where 子句(EF 代码优先)。
您必须显式定义类型变量,但下一段代码将帮助您解决您的场景:
// define new expression that get an Order object and returns string value
Expression<Func<Order, string>> p = x => x.Customer.Name;
var c = new MyClass<Order>();
// Compile the expression to the Func then invoke it and call extra criteria
c.Criteria = o => p.Compile().Invoke(o).StartsWith("SomeText");
没有表达式的解决方案稍微简单一点:
Func<Order, string> p = x => x.Customer.Name;
var c = new MyClass<Order>();
c.Criteria = o => p(o).StartsWith("SomeText");
您也可以在 MyClass
中使用 Func<>
代替 Expression<>
:
public MyClass<T>
{
Func<T,bool> Criteria {get; set;}
}
我看不出在这里使用 Expression
有什么好处。顺子如何 Func
?
public class MyClass<T>
{
public Func<T, string, bool> Criteria { get; set; }
}
然后...
var myCustomer = new MyClass<Customer>
{
Criteria = (c, s) => c.Name.StartsWith(s)
};
var customer = new Customer { Name = "Bob" };
var x = myCustomer.Criteria(customer, "B");
如果你想要一个表达式,那么你可以使用LinqKit来执行以下操作:
Expression<Func<Customer, string>> p = x => x.Name;
var c = new MyClass<Customer>();
c.Criteria = x => p.Invoke(x).StartsWith("asd"); //Reuse p expression
c.Criteria = c.Criteria.Expand();
Invoke
是LinqKit提供的一种扩展方法,可以帮助你轻松编写表达式。
调用 Expand
方法后,c.Criteria
将包含一个与您执行此操作完全相同的表达式:
c.Criteria = x => x.Name.StartsWith("asd");
您可以使用以下辅助函数(可以给它们起一个更好的名字,但这不是必需的):
public static class ExpressionUtils
{
public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
{
var body = new ParameterExpressionReplacer { source = resultSelector.Parameters[0], target = source.Body }.Visit(resultSelector.Body);
var lambda = Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
return lambda;
}
public static Expression<Func<TOuter, TResult>> ApplyTo<TInner, TResult, TOuter>(this Expression<Func<TInner, TResult>> source, Expression<Func<TOuter, TInner>> innerSelector)
{
return innerSelector.Bind(source);
}
class ParameterExpressionReplacer : ExpressionVisitor
{
public ParameterExpression source;
public Expression target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == source ? target : base.VisitParameter(node);
}
}
}
让我们看看示例表达式如何
c.Criteria = x => x.Name.StartsWith("SomeTexts");
可以由两个不同的部分构建。
如果你有
Expression<Func<Customer, string>> e = x => x.Name;
然后
c.Criteria = e.Bind(x => x.StartsWith("SomeTexts"));
或者如果你有这个
Expression<Func<string, bool>> e = x => x.StartsWith("SomeTexts");
然后
c.Criteria = e.ApplyTo((Customer x) => x.Name);
如果你有两个表达式,那么你可以使用两个函数中的任何一个,因为 a.Bind(b)
等价于 b.ApplyTo(a)
.
我有以下代码:
public class MyClass<T>
{
Expression<Func<T,bool>> Criteria {get; set;}
}
public class Customer
{
//..
public string Name {get; set;}
}
并按如下方式使用它:
var c = new MyClass<Customer>();
c.Criteria = x.Name.StartWith("SomeTexts");
有没有办法定义这样的东西:
? p = x=>x.Customer.Name;
var c = new MyClass<Customer>();
c.Criteria = p => p.StartWith("SomeTexts");
我使用 Expression<Func<T,bool>>
将其用作 linq to entities
查询中的 where 子句(EF 代码优先)。
您必须显式定义类型变量,但下一段代码将帮助您解决您的场景:
// define new expression that get an Order object and returns string value
Expression<Func<Order, string>> p = x => x.Customer.Name;
var c = new MyClass<Order>();
// Compile the expression to the Func then invoke it and call extra criteria
c.Criteria = o => p.Compile().Invoke(o).StartsWith("SomeText");
没有表达式的解决方案稍微简单一点:
Func<Order, string> p = x => x.Customer.Name;
var c = new MyClass<Order>();
c.Criteria = o => p(o).StartsWith("SomeText");
您也可以在 MyClass
中使用 Func<>
代替 Expression<>
:
public MyClass<T>
{
Func<T,bool> Criteria {get; set;}
}
我看不出在这里使用 Expression
有什么好处。顺子如何 Func
?
public class MyClass<T>
{
public Func<T, string, bool> Criteria { get; set; }
}
然后...
var myCustomer = new MyClass<Customer>
{
Criteria = (c, s) => c.Name.StartsWith(s)
};
var customer = new Customer { Name = "Bob" };
var x = myCustomer.Criteria(customer, "B");
如果你想要一个表达式,那么你可以使用LinqKit来执行以下操作:
Expression<Func<Customer, string>> p = x => x.Name;
var c = new MyClass<Customer>();
c.Criteria = x => p.Invoke(x).StartsWith("asd"); //Reuse p expression
c.Criteria = c.Criteria.Expand();
Invoke
是LinqKit提供的一种扩展方法,可以帮助你轻松编写表达式。
调用 Expand
方法后,c.Criteria
将包含一个与您执行此操作完全相同的表达式:
c.Criteria = x => x.Name.StartsWith("asd");
您可以使用以下辅助函数(可以给它们起一个更好的名字,但这不是必需的):
public static class ExpressionUtils
{
public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
{
var body = new ParameterExpressionReplacer { source = resultSelector.Parameters[0], target = source.Body }.Visit(resultSelector.Body);
var lambda = Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
return lambda;
}
public static Expression<Func<TOuter, TResult>> ApplyTo<TInner, TResult, TOuter>(this Expression<Func<TInner, TResult>> source, Expression<Func<TOuter, TInner>> innerSelector)
{
return innerSelector.Bind(source);
}
class ParameterExpressionReplacer : ExpressionVisitor
{
public ParameterExpression source;
public Expression target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == source ? target : base.VisitParameter(node);
}
}
}
让我们看看示例表达式如何
c.Criteria = x => x.Name.StartsWith("SomeTexts");
可以由两个不同的部分构建。
如果你有
Expression<Func<Customer, string>> e = x => x.Name;
然后
c.Criteria = e.Bind(x => x.StartsWith("SomeTexts"));
或者如果你有这个
Expression<Func<string, bool>> e = x => x.StartsWith("SomeTexts");
然后
c.Criteria = e.ApplyTo((Customer x) => x.Name);
如果你有两个表达式,那么你可以使用两个函数中的任何一个,因为 a.Bind(b)
等价于 b.ApplyTo(a)
.