-1

I am looking for a way to iterate through a collection of Linq methods, previously built, and then manipulate them, like change the expression tree.

Here is an example:

enter image description here

what i simply want to do is a forEach (or something of sorts) to get a cycle where:

i get the linq .Where(e => e.Summary == "sum") in the first position

i get the linq .Where(l => l.Description == "asd") in the second position

i get the linq .Take(2) in the last iteration

Is this possible? Most likely with reflection, but i could do it or find it.

Other Example

Using the example above, i want to peek that Ienumerable/Iqueryable and get the first where, getting the equivalent result:

var c = list
        .Where(e => e.Summary == "sum");

Let me emphasise, i want this via runtime, with the Ienumerable/Iqueryable as an input.

Thank you,

Júlio Almeida
  • 1,539
  • 15
  • 23
  • Is this maybe the direction you are going for: https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.expression?view=net-5.0 ? See also: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/ – Fildor Jun 07 '21 at 09:42
  • 1
    You want to loop on what to do what? What do you call a cycle? What is the link with linq chaining or reflexion? –  Jun 07 '21 at 09:42
  • Sorry, but your question is clear enough. Please provide example of what you are actually trying to accomplish. – Mat J Jun 07 '21 at 09:44
  • Mind: _"Expression trees should be immutable. This means that if you want to modify an expression tree, you must construct a new expression tree by copying the existing one and replacing nodes in it."_ - [Immutability of Expression Trees](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/#immutability-of-expression-trees) – Fildor Jun 07 '21 at 09:44
  • I added an example that maybe can be more simple to grasp – Júlio Almeida Jun 07 '21 at 10:46
  • I will close this question. I want to make this approach in EF core, but i was removing it altogether to avoid confusion, but was a mistake. I will simply make a new implementation of the DBContext and make some sort of wrapper. thank you – Júlio Almeida Jun 07 '21 at 11:07

1 Answers1

1

I think the easiest way to do this is by creating new functions (such as _Where(), _Take()) that just call the Where and Take function, but also save the fact that you called them and with which arguments. Then later you can read them again.

The downside of this is that you need to somehow store which calls belong to which. I.e. if you have two statements list.Where().Where() and list.Where().Take() you need to save which calls belong to the first line and which belong to the second.

You could also try to make these sepcialized functions return a tuple containing both the result and the call information. Then you should create two functions for each linq method: one which just accepts an enumerable and one which accepts a tuple of an enumerable and the call information from before. This way you can add the new call information to the old one. If you use extension functions, you have to extend the enumerable and the tuple of course.

Edit:

Let me add an example of my thinking... Note that it is difficult to perfectly serialize the function given in the Where function. It is possible, check for example Convert Func delegate to a string

Overloads.cs:

public static class Overloads
{
    public static (IEnumerable<T>, List<string>) Where_<T>(this IEnumerable<T> inp, Func<T, bool> predicate)
    {
        return (inp.Where(predicate), new List<string> { ".Where(...)" });
    }

    public static (IEnumerable<T>, List<string>) Where_<T>(this (IEnumerable<T>, List<string>) inp, Func<T, bool> predicate)
    {
        inp.Item2.Add(".Where(...)");
        return (inp.Item1.Where(predicate), inp.Item2);
    }

    public static (IEnumerable<T>, List<string>) Take_<T>(this IEnumerable<T> inp, int count)
    {
        return (inp.Take(count), new List<string> { $".Take({count})" });
    }

    public static (IEnumerable<T>, List<string>) Take_<T>(this (IEnumerable<T>, List<string>) inp, int count)
    {
        inp.Item2.Add($".Take({count})");
        return (inp.Item1.Take(count), inp.Item2);
    }
}

Example run:

public static void Main(string[] args)
{
    var a = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    var (result, calls) = a
        .Where_(x => x % 2 == 0)
        .Where_(x => x % 3 == 0)
        .Take_(1);

    Console.WriteLine(string.Join(", ", result));
    Console.WriteLine(string.Join(", ", calls));
}

Output:

0
.Where(...), .Where(...), .Take(1)
BornoBob
  • 13
  • 6
  • Good answer. I think for me the best of two worlds would be in fact do a new override implementation of this extension methods, that store this behaviors. What you think ? – Júlio Almeida Jun 07 '21 at 10:34
  • I think you should go for the approach with tuples for cleanliness, please check my answer... HTH – BornoBob Jun 07 '21 at 13:19