Build complex Lambda Expression

Asked

Viewed 800 times

11

I’m trying to build a Lambda Expression that’s a little complex and I’m having difficulties.

The goal is this expression:

items = items.Where(x => sValues.Any(y => x.Contract_Rates.Select(z => z.CostCenterId).Contains(y)));

However, in RunTime, i don’t know the name of the list that in the example is Contract_Rates, the point is that I need to scan the collections of an object, I already have a method that fills the variable Collections (displayed in the code below) and check if this is a collection that implements IHasCostCenter, positive case, carry out the Lambda Expression quoted.

What I’ve got so far is:

var sValues = User.CostCenters.Select(x => x.CostCenterId).ToList();

ParameterExpression Parameter = Expression.Parameter(typeof(Contrato), "x");

foreach (var item in Collections.Select(x => x).Where(x => x.GetInterfaces().Any(y => y == typeof(IHasCostCenter))))
{
    // Obtenho o nome da lista (i.e. 'Contract_Rates')
    var PropertyName = typeof(T).GetProperties().FirstOrDefault(x => x.Name.Contains(item.Name)).Name;

    Expression Property = Expression.Property(Parameter, typeof(T).GetProperty(PropertyName));
    LambdaExpression Column = Expression.Lambda(Property, Parameter);
    Expression Select = Expression.Call(
        typeof(Queryable),
        "Select",
        new Type[] { items.ElementType, Column.Body.Type },
        items.Expression,
        Column);

    Expression Contains = Expression.Call(
        typeof(Enumerable),
        "Contains",
        new[] { sValuesType },
        Expression.Constant(sValues, sValues.GetType()),
        Select);

    MethodCallExpression Where = Expression.Call(
        typeof(Queryable),
        "Where",
        new Type[] { items.ElementType },
        items.Expression,
        Expression.Lambda(Contains, Parameter));

    items = items.Provider.CreateQuery<T>(Where);

However I cannot correctly develop how I get the expression SELECT x.Contract_Rates.Select(y => y.CostCenterId) and how I get the expression ANY sValues.Any(y => x.Contrato_Rateios.Select(z => z.CentroCustoId).Contains(y))

Any help will be appreciated.

  • Why you necessarily need to use lambda Expressions?

  • I can’t use it Lambda normally because I don’t know the name of the attributes, I can’t use Reflection other than for Lambda Expression. Nor can I convert the object during the use of Lambda (for IHasCostCenter, for example) because the object after converted generates error when returning to the entity T. Can you see some other way?

  • It seems to me a code too complicated for what you want to do. Maybe understanding the issue of a macro scope I can help you better.

  • Thank you for your attention @Ciganomorrisonmendez. The situation is as follows: Imagine that each user has a list of Costcenter (sValues). Based on this list I need to filter the tuples of the classes that he can see, and this can only happen if the entity implements Ihascostcenter, because not all classes need this filter, While if the parent class does not implement Ihascostcenter but some daughter property (or list, in this case) implement, I also need to filter the parent class tuples by the daughters. Got it?

  • Don’t you think it’s too complicated to filter the visibility of a record using an interface? I would use something other than reflection for that.

  • this question is as active. It still needs an answer?

  • Hello @Fernandomondo, the issue has not yet been resolved, I am particularly using an approach in which it is necessary to name the property as I have not found solution to this specific way.

  • Your first expression can be simplified. I know it doesn’t answer, but sometimes it can help in the solution. items = items.Where(x => sValues.Any(y => x.Contract_Rates.Any(z => z.CostCenterId == y));

Show 3 more comments

1 answer

0

The query you are wanting to build (perhaps) is not (is) necessary. You can achieve the same result using the join among the collections items and sValues.

See an example below:

var result = from x in items
             join y in sValues on x.Contract_Rates.Select(z => z.CostCenterId).Contains(y)
             select x;

Edit 1

After your comment, I understand your problem better. It might be easier to solve your problem with OOP (if you have access to the collection class Items). The alternative would be:

In the classes that should provide this behavior, you implement an interface requiring the provision of a method (which you will always know the name) to obtain the Ihascostcenter collection:

public interface IHasCostCenterAvailable {

   IHasCostCenter GetIHasCostCenterCollection();

   // outra opção
   //IHasCostCenter IHasCostCenterCollection {get;set;}
}

public class CollectionItems: IHasCostCenterAvailable {

   IHasCostCenter GetIHasCostCenterCollection()
   {
      return this.Contract_Rates;
   }
}


public class OutraCollectionItems: IHasCostCenterAvailable {

   IHasCostCenter GetIHasCostCenterCollection()
   {
      return this.OutroNomeQualquerDeColecao;
   }
}


// Exemplo de preenchimento
var items = new List<IHasCostCenterAvailable>();
items.Add(new CollectionItems());
items.Add(new OutraCollectionItems());

// Nova consulta.
var result = from x in items
             join y in sValues on x.GetIHasCostCenterCollection().Select(z => z.CostCenterId).Contains(y)
             select x;

In this second suggestion, you do not care about the names of the collections, because you will access the desired collection always by the interface IHasCostCenterAvailable. So your problem, knowing the name of the collection, ends.

Edit 2 If you don’t have access to the collection class items directly, there are some other ways to access it, which could be through:

  • visitor standard;
  • methods Extensions;
  • reflection.
  • First I would like to thank you for the help. I can even try this approach, the point is that through the initial expression (items = items.Where(x => sValues.Any(y => x.Contract_Rates.Select(z => z.CostCenterId).Contains(y)));) I get the necessary result. I just can’t assemble the expression dynamically, in cases where I don’t know the name of the property that contains the list of IHasCostCenter. In the example I make it clear that it is the property Contract_Rates, but dynamically I won’t know.

  • See the second example that I have provided now. Maybe he can help you.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.