Need to && together an indeterminate number of Func<TEntity, bool>

c#, delegates, lambda

Solution

Instead of trying to combine the delegates this way, you could build new delegates that use the existing one with your AND condition:

Func<SurveyUserView, bool> resultFilter = (suv) => true;

if (filters.ContainsKey("RegionFilter"))
{
    var tmpFilter = resultFilter;
    // Create a new Func based on the old + new condition
    resultFilter = (suv) => tmpFilter(suv) && suv.Region == filters["RegionFilter"];
}

if (filters.ContainsKey("LanguageFilter"))
{
   // Same as above...

//... Continue, then:

return resultFilter;

That being said, it may be easier to pass your original `IQueryable<SurveyUserView>` or `IEnumerable<SurveyUserView>` into this method, and just add `.Where` clauses directly to filter. You could then return the final query without executing it, with the filters added on.

Problem

I'm trying to find a good way to cumulatively apply up to 5 Func's to the same IEnumerable. Here is what I came up with: ``` private Func<SurveyUserView,bool> _getFilterLambda(IDictionary<string, string> filters) { Func<SurveyUserView, bool> invokeList = delegate(SurveyUserView surveyUserView) { return surveyUserView.deleted != "deleted"; }; if (filters.ContainsKey("RegionFilter")) { invokeList += delegate(SurveyUserView surveyUserView) { return surveyUserView.Region == filters["RegionFilter"]; }; } if (filters.ContainsKey("LanguageFilter")) { invokeList += delegate(SurveyUserView surveyUserView) { return surveyUserView.Locale == filters["LanguageFilter"]; }; } if (filters.ContainsKey("StatusFilter")) { invokeList += delegate(SurveyUserView surveyUserView) { return surveyUserView.Status == filters["StatusFilter"]; }; } if (filters.ContainsKey("DepartmentFilter")) { invokeList += delegate(SurveyUserView surveyUserView) { return surveyUserView.department == filters["DepartmentFilter"]; }; } return invokeList; } ``` I thought that it would apply these in a cumulative fashion, however, I can see from the results that it's actually just applying the last one (DepartmentFilter). There are 2^4 possible combinations so brute-force if/elses are not going to work. (I want to AND using a particular lambda only when the corresponding key is present in the Dictionary.) EDIT: Here is the solution that I accepted, but it causes a StackOverflowException when it is evaluated. Anybody see why? ``` private Func<SurveyUserView,bool> _getFilterLambda(IDictionary<string, string> filters ) { Func<SurveyUserView, bool> resultFilter = (suv) => suv.deleted != "deleted"; if (filters.ContainsKey("RegionFilter")) { Func<SurveyUserView, bool> newFilter = (suv) => resultFilter(suv) && suv.Region == filters["RegionFilter"]; resultFilter = newFilter; } if (filters.ContainsKey("LanguageFilter")) { Func<SurveyUserView, bool> newFilter = (suv) => resultFilter(suv) && suv.Locale == filters["LanguageFilter"]; resultFilter = newFilter; } if (filters.ContainsKey("StatusFilter")) { Func<SurveyUserView, bool> newFilter = (suv) => resultFilter(suv) && suv.Status == filters["StatusFilter"]; resultFilter = newFilter; } if (filters.ContainsKey("DepartmentFilter")) { Func<SurveyUserView, bool> newFilter = (suv) => resultFilter(suv) && suv.department == filters["DepartmentFilter"]; resultFilter = newFilter; } return resultFilter; } ``` EDIT: Here is the very nice explanation of why this resulted in a StackOverflowException from friend and mentor Chris Flather- The important thing to understanding why the infinite recursion occurs is understanding when the symbols in a lambda are resolved (i.e. at runtime and not at definition). Take this simplified example: ``` Func<int, int> demo = (x) => x * 2; Func<int, int> demo2 = (y) => demo(y) + 1; demo = demo2; int count = demo(1); ``` If it were resolved statically at definition this would work and be the same as: ``` Func<int, int> demo2 = (y) => (y * 2) + 1; Int count = demo2(1); ``` But it doesn’t actually attempt to figure out what the demo embedded in demo2 does until runtime – at which time demo2 has been redefined to demo. Essentially the code now reads: ``` Func<int, int> demo2 = (y) => demo2(y) + 1; Int count = demo2(1); ```

Original source