Expression.Call e Count

.net c# dynamic expression-trees linq

Domanda

Sto cercando un modo per farlo seguendo dinamicamente:

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

Vorrei creare un'espressione dinamica per il lato sinistro:

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

Grazie!

NOTE AGGIORNATE:

Nota: il risultato finale deve essere

Expression<Func<T, bool>>

Versione semplice:

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

Versione estesa:

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

Quindi, spero sia chiaro ora perché ho bisogno di Expression per il lato sinistro nel mio post originale. Cercando di rendere questo più ASCIUTTO possibile.

PS Per non renderlo troppo confuso ecco perché penso di aver bisogno di fare Ñ'Expression.Call (...) Ñ ': Quando eseguo il codice seguente e lo interrompo per vedere DebugView, noto questo:

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

Risposta popolare

Penso che questo dovrebbe avvicinarti a quello che stai cercando:

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

Chiamandolo così:

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

... produce la seguente Expression<Func<Subscription,bool>> :

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


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché