使用字典作为过滤方法参数的动态 LINQ 函数

Dynamic LINQ function using dictionary as a Parameter to filter method

我是反射区的新手。我必须通过使用它的键和值来过滤具有字典的实体列表,如下所示

public class Person
{
    public string Name { get; set; }
    public Dictionary<string,string> SecQuestions { get; set; }
}

以下扩展在 dll 中可用,无法修改

public static class extensions
{
    public static List<Person> FilterMe(this List<Person> Persons, Func<Person,bool> predicate)
    {
        // Logic to filter the persion list
        return Persons;
    }
}

因此我必须使用以下代码调用上述方法

persons.FilterMe(xy => xy.SecQuestions.Any(x => x.Key == "PlaceOfBirth" && x.Value == "Madurai"));

我需要知道如何创建

xy => xy.SecQuestions
    .Any(x => x.Key == "PlaceOfBirth" && x.Value == "Madurai")

动态使用表达式构建器作为参数传递给扩展方法。谢谢

这里似乎根本不需要使用表达式构建器,或者实际上不需要任何扩展方法(看起来您是在重新发明轮子)。像下面这样的东西应该适合你的需要:

var filteredPeople = people.Where(person => person.SecQuestions[key] == value);

如何确定 keyvalue 由您决定。它可以是方法参数或用户输入或其他任何东西。

var personP = Expression.Parameter(typeof(Person), "xy");            // Creates xy Parameter
var SecQuestionsProp = Expression.Property(personP, "SecQuestions"); // Creates xy.SecQuestions

var anyMethodInfo = typeof(Enumerable).GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
    .Where(m => m.Name == "Any" && m.IsGenericMethod) // Search for Any methods...
    .Select(m => new {
                        Method = m,
                        Params = m.GetParameters(),
                        Args = m.GetGenericArguments()
                     })
    .Where(x => x.Args.Length == 1 
        && x.Params.Length == 2 
        && x.Params[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(x.Args)
        && x.Params[1].ParameterType == typeof(Func<,>).MakeGenericType(new Type[] { x.Args.First(), typeof(bool) })) // Get the one defined as Any<TSource>(IEnumerable<TSource>, Func<TSource, bool>)
    .Select(x => x.Method)
    .First();

var keyValuePairP = Expression.Parameter(typeof(KeyValuePair<string, string>), "x"); // Creates x Parameter
var KeyProp = Expression.Property(keyValuePairP, "Key");                             // Creates x.Key
var keyComparisonValue = Expression.Constant("PlaceOfBirth");                        // Creates the value that will be compared to x.Key
var keyComparison = Expression.Equal(KeyProp, keyComparisonValue);                   // Creates the comparison (x.Key == "PlaceOfBirth")

var ValueProp = Expression.Property(keyValuePairP, "Value");                         // Creates x.Value
var valueComparisonValue = Expression.Constant("Madurai");                           // Creates the value that will be compared to x.Value
var valueComparison = Expression.Equal(ValueProp, valueComparisonValue);             // Creates the comparison (x.Value == "Madurai")

var anyPredicate = Expression.Lambda(Expression.AndAlso(keyComparison, valueComparison), new ParameterExpression[] { keyValuePairP }); // Creates x => x.Key == "PlaceOfBirth" && x.Value == "Madurai"

var filterMeMethod = Expression.Lambda<Func<Person, bool>>
    (Expression.Call(anyMethodInfo.MakeGenericMethod(new Type[] { typeof(KeyValuePair<string, string>) })
                    , new Expression[] { SecQuestionsProp, anyPredicate })
     , personP);  //Creates xy => xy.SecQuestions.Any(x => x.Key == "PlaceOfBirth" && x.Value == "Madurai")

var r1 = persons.FilterMe(filterMeMethod.Compile());  // Calls FilterMe with xy => xy.SecQuestions.Any(x => x.Key == "PlaceOfBirth" && x.Value == "Madurai") as parameter

如果您需要您比较的值是动态的,那么您可以让语言来完成繁重的工作:

public Expression BuildComparison(string key, string value)
{
    return Expression<Func<Person, bool>> exp = 
                 x => x.SecQuestions.Any(y => y.Key == key && y.Value == value);
}

如果我误解了您的要求,我们深表歉意。