防爆pression.Call和计数 [英] Expression.Call and Count
问题描述
我正在寻找一个方式做动态如下:
变种Q = context.Subscription
.INCLUDE(客户)
.INCLUDE(发票)
其中,(S => s.Client.Invoices.Count(ⅰ=> i.InvoiceID == SomeInt)大于0);
我要动态地构建EX pression的左侧:
防爆pression左= S => s.Client.Invoices.Count(ⅰ=> i.InvoiceID == iSomeVar); //!
防爆pression右=前pression.Constant(0);
VAR二进制=前pression.GreaterThan(左,右);
谢谢!
修订注意:
请注意:最终的结果一定是
防爆pression< Func键< T,布尔>>
简易版:
//给出明确的想法,所有我想要实现的是确定
//使用已知的路径在引用表中是否具体的记录存在。
//最后,我想对下面的函数(它的伟大工程
//的方式,但对于简单的操作)
静态防爆pression CreateEx pression< T>(字符串的PropertyPath,
对象为PropertyValue,
ParameterEx pression parameterEx pression)
{
的PropertyInfo财产= typeof运算(T).GetProperty(propertyName的);
MemberEx pression左=前pression.Property(parameterEx pression,财产);
ConstantEx pression右=前pression.Constant(0);
BinaryEx pression二进制=前pression.GreaterThan(左,右);
返回二进制;
}
//我要调用这个函数,并得到确切结果如下图所示:
防爆pression结果=
CreateEx pression<认购>(Client.Invoices.InvoiceID
theID,
valueSelector.Parameters.Single());
//如果结果将是:
// T => t.Client.Invoices.Count(I => i.InvoiceID == theID)> 0;
扩展版:
// 1)我使用Silverlight 4中,EF,RIA。
// 2)在服务器端我有一个函数GetSubscriptionsByCriteria
//看起来这件事:
公众的IQueryable<认购> GetSubscriptionsByCriteria(...)
{
VAR的查询= this.ObjectContext.Subscriptions.Include(客户)
.INCLUDE(Client.Invoices);
VAR标准= BuildCriteria(...);
返回query.Where(标准)
}
// 3)BuildCriteria(...)函数集防爆pressions和
//聚集入单个实施例pression具有不同
// AND / OR条件,这样的事情:
公共防爆pression< Func键<认购,布尔>> BuildCriteria(
名单< SearchFilter>过滤器,
防爆pression< Func键<认购,布尔>> valueSelector)
{
名单<防爆pression> filterEx pressions =新的名单,其中,前pression>();
...
防爆pression EXPR = CreateEx pression<认购>(
sfItem.DBPropertyName,
sfItem.DBPropertyValue,
paramEx pression,
sf.SearchCondition);
filterEx pressions.Add(表达式);
...
VAR filterBody =
filterEx pressions.Aggregate<防爆pression>(
(积累,等于)=>防爆pression.And(积累,等于));
返回前pression
.Lambda< Func键<认购,布尔>>(filterBody,paramEx pression);
}
// 4)这里是CreateEx pression功能的简化版本:
静态防爆pression CreateEx pression< T>(字符串propertyName的,
对象为PropertyValue,
ParameterEx pression paramEx pression)
{
的PropertyInfo财产= typeof运算(T).GetProperty(propertyName的);
ConstantEx pression右=前pression.Constant(0);
MemberEx pression左=前pression.Property(paramEx pression,财产);
返回二进制=前pression.Equals(左,右);
}
所以,我希望这是明确的,现在为什么需要防爆pression的左侧在原来的职位。试图使这个尽可能的干燥。
P.S。不要弄得太混乱这里就是为什么我认为我需要做ёExpression.Call(...)ё: 当我运行下面的code和打破它,看看我的DebugView注意到这一点:
防爆pression< Func键<认购,布尔>> predicate =
T => t.Client.Invoices.Count(ⅰ=> i.InvoiceID == 5)> 0;
BinaryEx pression EQ =(BinaryEx pression)predicate.Body;
VAR左= eq.Left; //< - 看到调试视图
无功权= eq.Right;
//调试视图:
//参数:数= 2
// [0] = {} t.Client.Invoices
// [1] = {I => (i.InvoiceID == 5)}
// DebugView中:.CALL System.Linq.Enumerable.Count(
//($ t.Client).ClientInvoices,
// .Lambda#Lambda1&其中; System.Func`2 [SLApp.Web.Invoice,可选System.Boolean]≥)
// .Lambda#Lambda1&其中; System.Func`2 [SLApp.Web.Invoice,可选System.Boolean]≥
//(SLApp.Web.ClientInvoice $ I){$ i.ClientInvoiceID == 5}
我想这应该让你更接近你要去什么:
静态防爆pression< Func键< T,布尔>> CreateAnyEx pression< T,T2>(字符串的PropertyPath,
防爆pression< Func键< T2,布尔>> matchEx pression)
{
VAR类型= typeof运算(T);
变种parameterEx pression =实施例pression.Parameter(类型,S);
变种propertyNames = propertyPath.Split('。');
防爆pression propBase = parameterEx pression;
的foreach(在propertyNames VAR propertyName的)
{
的PropertyInfo财产= type.GetProperty(propertyName的);
propBase =前pression.Property(propBase,属性);
TYPE = propBase.Type;
}
变种ITEMTYPE = type.GetGenericArguments()[0];
//。任何(...)比.Count之间(...)&GT更好; 0
VAR anyMethod = typeof运算(可枚举).GetMethods()
。单(M => m.Name ==任何&放大器;&安培; m.GetParameters()长度== 2)
.MakeGenericMethod(ITEMTYPE);
VAR callToAny =前pression.Call(anyMethod,propBase,matchEx pression);
返回前pression.Lambda< Func键< T,布尔>>(callToAny,parameterEx pression);
}
调用它是这样的:
CreateAnyEx pression<认购,发票>(Client.Invoices,I => i.InvoiceID == 1)
......将产生以下防爆pression< Func键<认购,布尔>>
:
S => s.Client.Invoices.Any(ⅰ=>(i.InvoiceID == 1))
I'm looking for a way to do following dynamically:
var q = context.Subscription
.Include("Client")
.Include("Invoices")
Where(s=>s.Client.Invoices.Count(i=>i.InvoiceID == SomeInt) > 0);
I would like to build expression dynamically for the left side:
Expression left = s => s.Client.Invoices.Count(i => i.InvoiceID == iSomeVar); //!
Expression right = Expression.Constant(0);
var binary = Expression.GreaterThan(left, right);
Thanks!
UPDATED NOTES:
Please note: The end result must be
Expression<Func<T, bool>>
Simple version:
// To give clear idea, all what I want to achieve is to determine
// whether specific record exists in reference table using known Path.
// Ultimately I want to extend following function (which works great by
// the way, but for simple operations)
static Expression CreateExpression<T>(string propertyPath,
object propertyValue,
ParameterExpression parameterExpression)
{
PropertyInfo property = typeof(T).GetProperty(propertyName);
MemberExpression left = Expression.Property(parameterExpression, property);
ConstantExpression right = Expression.Constant(0);
BinaryExpression binary = Expression.GreaterThan(left, right);
return binary;
}
// And I want to call this function and get result exactly as shown below:
Expression result =
CreateExpression<Subscription>("Client.Invoices.InvoiceID",
theID,
valueSelector.Parameters.Single());
// Where result will be:
// t => t.Client.Invoices.Count(i => i.InvoiceID == theID) > 0;
Extended version:
// 1) I'm using Silverlight 4, EF, RIA.
// 2) At the server side I have a function GetSubscriptionsByCriteria
// that looks about it:
public IQueryable<Subscription> GetSubscriptionsByCriteria(...)
{
var query = this.ObjectContext.Subscriptions.Include("Client")
.Include("Client.Invoices");
var criteria = BuildCriteria(...);
return query.Where(criteria)
}
// 3) BuildCriteria(...) function gathers Expressions and
// aggregates it into the single Expression with different
// AND/OR conditions, something like that:
public Expression<Func<Subscription, bool>> BuildCriteria(
List<SearchFilter> filters,
Expression<Func<Subscription, bool>> valueSelector)
{
List<Expression> filterExpressions = new List<Expression>();
...
Expression expr = CreateExpression<Subscription>(
sfItem.DBPropertyName,
sfItem.DBPropertyValue,
paramExpression,
sf.SearchCondition);
filterExpressions.Add(expr);
...
var filterBody =
filterExpressions.Aggregate<Expression>(
(accumulate, equal) => Expression.And(accumulate, equal));
return Expression
.Lambda<Func<Subscription, bool>>(filterBody, paramExpression);
}
// 4) Here is the simplified version of CreateExpression function:
static Expression CreateExpression<T>(string propertyName,
object propertyValue,
ParameterExpression paramExpression)
{
PropertyInfo property = typeof(T).GetProperty(propertyName);
ConstantExpression right = Expression.Constant(0);
MemberExpression left = Expression.Property(paramExpression, property);
return binary = Expression.Equals(left, right);
}
So, I hope it's clear now why do I need Expression for the left side in my original post. Trying to make this as DRY as possible.
P.S. Not to make it too confusing here is why I think I need to do ёExpression.Call(...)ё: When I run following code and break it to see DebugView I notice this:
Expression<Func<Subscription, bool>> predicate =
t => t.Client.Invoices.Count(i => i.InvoiceID == 5) > 0;
BinaryExpression eq = (BinaryExpression)predicate.Body;
var left = eq.Left; // <-- See DEBUG VIEW
var right = eq.Right;
// DEBUG VIEW:
// Arguments: Count = 2
// [0] = {t.Client.Invoices}
// [1] = {i => (i.InvoiceID == 5)}
// DebugView: ".Call System.Linq.Enumerable.Count(
// ($t.Client).ClientInvoices,
// .Lambda#Lambda1<System.Func`2[SLApp.Web.Invoice,System.Boolean]>)
// .Lambda#Lambda1<System.Func`2[SLApp.Web.Invoice,System.Boolean]>
// (SLApp.Web.ClientInvoice $i){ $i.ClientInvoiceID == 5 }"
I think this should get you closer to what you're going for:
static Expression<Func<T, bool>> CreateAnyExpression<T, T2>(string propertyPath,
Expression<Func<T2, bool>> matchExpression)
{
var type = typeof(T);
var parameterExpression = Expression.Parameter(type, "s");
var propertyNames = propertyPath.Split('.');
Expression propBase = parameterExpression;
foreach(var propertyName in propertyNames)
{
PropertyInfo property = type.GetProperty(propertyName);
propBase = Expression.Property(propBase, property);
type = propBase.Type;
}
var itemType = type.GetGenericArguments()[0];
// .Any(...) is better than .Count(...) > 0
var anyMethod = typeof(Enumerable).GetMethods()
.Single(m => m.Name == "Any" && m.GetParameters().Length == 2)
.MakeGenericMethod(itemType);
var callToAny = Expression.Call(anyMethod, propBase, matchExpression);
return Expression.Lambda<Func<T, bool>>(callToAny, parameterExpression);
}
Calling it like this:
CreateAnyExpression<Subscription, Invoice>("Client.Invoices", i => i.InvoiceID == 1)
... yields the following Expression<Func<Subscription,bool>>
:
s => s.Client.Invoices.Any(i => (i.InvoiceID == 1))
这篇关于防爆pression.Call和计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!