2011-05-14 55 views
3

我正在寻找一个方式做动态如下:Expression.Call和计数

var q = context.Subscription 
       .Include("Client") 
       .Include("Invoices") 
       Where(s=>s.Client.Invoices.Count(i=>i.InvoiceID == SomeInt) > 0); 

我想动态生成式的左侧:

Expression left = s => s.Client.Invoices.Count(i => i.InvoiceID == iSomeVar); //! 
Expression right = Expression.Constant(0); 
var binary = Expression.GreaterThan(left, right); 

谢谢!

经更新的票据:

请注意:最终结果必须是

Expression<Func<T, bool>> 

简易版:

// 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; 

扩展版本:

// 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); 
} 

所以,我希望现在很清楚为什么我需要在原始文章中左侧的表达式。试图尽可能做到这一点。

P.S.不要让这太混乱了,所以我觉得我需要做ёExpression.Call(...)ё: 当我运行下面的代码,并打破它,看看我的DebugView注意到这一点:

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 }" 

回答

0

这里有一个工作计划,做我想你想要的。它定义了一个函数,它获取集合中的整数属性的路径以及整数值。然后检查该集合是否具有该值的Count> 0。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Linq.Expressions; 
using System.Reflection; 
using System.Collections; 

namespace Test_Console 
{ 
    public class Subscription 
    { 
     public int Id { get; set; } 
     public Client Client { get; set; } 
    } 

    public class Client 
    { 
     public ICollection<Invoice> Invoices { get; set; } 
    } 

    public class Invoice 
    { 
     public int Id { get; set; } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var subscriptions = new[] 
      { 
       new Subscription { Id = 1, Client = new Client { Invoices = new [] { 
        new Invoice { Id = 1 }, 
        new Invoice { Id = 2 }, 
        new Invoice { Id = 5 } 
       } } }, 
       new Subscription { Id = 2, Client = new Client { Invoices = new [] { 
        new Invoice { Id = 4 }, 
        new Invoice { Id = 5 }, 
        new Invoice { Id = 5 } 
       } } }, 
       new Subscription { Id = 3, Client = new Client { Invoices = new Invoice[] { 
       } } }, 
      }; 

      var propertyPath = "Client.Invoices.Id"; 
      Console.WriteLine("What Id would you like to check " + propertyPath + " for?"); 
      var propertyValue = int.Parse(Console.ReadLine()); 
      var whereNumberOne = makeWhere<Subscription>(propertyPath, propertyValue); 

      Console.WriteLine("The following Subscription objects match:"); 
      foreach (var s in subscriptions.Where(whereNumberOne).ToList()) 
      { 
       Console.WriteLine("Id: " + s.Id); 
      } 
     } 

     private static Func<T, bool> makeWhere<T>(string propertyPath, int propertyValue) 
     { 
      string[] navigateProperties = propertyPath.Split('.'); 

      var currentType = typeof(T); 
      var functoidChain = new List<Func<object, object>>(); 
      functoidChain.Add(x => x); // identity function starts the chain 
      foreach (var nextProperty in navigateProperties) 
      { 
       // must be inside loop so the closer on the functoids works properly 
       PropertyInfo nextPropertyInfo; 

       if (currentType.IsGenericType 
       && currentType.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable))) 
       { 
        nextPropertyInfo = currentType.GetGenericArguments()[0].GetProperty(nextProperty); 
        functoidChain.Add(x => 
         ((IEnumerable<object>)x) 
         .Count(y => (int)nextPropertyInfo.GetValue(y, null) == propertyValue) 
        ); 
       } 
       else 
       { 
        nextPropertyInfo = currentType.GetProperty(nextProperty); 
        functoidChain.Add(x => nextPropertyInfo.GetValue(x, null)); 
       } 
       currentType = nextPropertyInfo.PropertyType; 
      } 
      // compose the functions together 
      var composedFunctoidChain = functoidChain.Aggregate((f, g) => x => g(f(x))); 
      var leftSide = new Func<T, int>(x => (int)composedFunctoidChain(x)); 
      return new Func<T, bool>(r => leftSide(r) > 0); 
     } 
    } 
} 
+0

谢谢,但您已链接的链接rovided不涉及我遇到的问题 – Max 2011-05-14 17:06:37

+0

非常感谢您回复此问题,但您提供的解决方案仍然无法解决问题。我真的需要一个左侧的表达式,我试图解释为什么在上面的帖子。 – Max 2011-05-15 05:47:43

+0

好吧,我现在正在做你正在做的,好的工作解释更好。我仍然不确定你需要表情,我会告诉你为什么。你总是在右侧使用> 0,所以这是恒定的。如果你有所不同,那么你应该把它变成动态的。使一切变得动态并不是更好的设计。在我看来,最好的设计就是尽可能简单和尽可能地解决问题。我会编辑我的答案,让我知道你在想什么。 – Milimetric 2011-05-15 11:13:24

0

我想这应该让你更接近你要什么:

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); 
} 

调用它像这样:

CreateAnyExpression<Subscription, Invoice>("Client.Invoices", i => i.InvoiceID == 1) 

...产生以下Expression<Func<Subscription,bool>>

s => s.Client.Invoices.Any(i => (i.InvoiceID == 1))