使用Lambda - . contains从表达式中获取集合的值

本文关键字:获取 集合 表达式 Lambda contains 使用 | 更新日期: 2023-09-27 18:03:58

从Expression with Lambda获取集合的值- .Contains

我试图从包含IQueryable成员的集合中获取所有值。然而,似乎不同的集合行为不同。

考虑以下清单:

 var sampleData = new List<MyClass> { new MyClass { MyMember = "a" }, new MyClass { MyMember = "b" }, new MyClass { MyMember = "c" } };
 IEnumerable<string> samplEnumerable = new List<string> { "b", "a" };
 List<string> sampleList = new List<string> { "b", "a" };
 var resultTest1 = sampleData.AsQueryable().GetCalledMemberAndValidValues(x => (new[] { "b", "a" }).Contains(x.MyMember));
 var resultTest2 = sampleData.AsQueryable().GetCalledMemberAndValidValues(x => sampleList.Contains(x.MyMember));
 var resultTest3 = sampleData.AsQueryable().GetCalledMemberAndValidValues(x => samplEnumerable.Contains(x.MyMember));

Extension Method

public static class ExtensionMethods
{
   public static dynamic GetCalledMemberAndValidValues<T>(this IQueryable<T> query, Expression<Func<T, bool>> predicate)
    {
      string parameterName = string.Empty;
      ArrayList values = new ArrayList();
  MethodCallExpression node = (MethodCallExpression)predicate.Body;
  if (predicate.Body.NodeType == ExpressionType.Call)
  {
    if ((node.Method.Name == "Contains"))
    {
      Type valueType = null;
      foreach (var obj in node.Arguments)
      {
        if (obj.NodeType == ExpressionType.MemberAccess && ((MemberExpression)obj).Expression.NodeType == ExpressionType.Parameter)
        {
          parameterName = ((MemberExpression)obj).Member.Name;
        }
        // The below block is valid for array[];
        if (obj.NodeType == ExpressionType.NewArrayInit)
        {
          values.AddRange(((NewArrayExpression)obj).Expressions);
        }
        // The below block is valid for IEnumerable<T>;
        if (obj.NodeType == ExpressionType.MemberAccess && ((MemberExpression)obj).Expression.NodeType == ExpressionType.Constant)
        {
          var value = (((MemberExpression) obj).Member as FieldInfo).GetValue(((obj as MemberExpression).Expression as ConstantExpression).Value);
          values.AddRange((ICollection)value);
        }
      }
      // The below block is valid for List<T>;
      if ((predicate.Body as MethodCallExpression).Object != null)
      {
        var obj = (MemberExpression)(predicate.Body as MethodCallExpression).Object;
        var value = (obj.Member as FieldInfo).GetValue((obj.Expression as ConstantExpression).Value);
        values.AddRange((ICollection)value);
      }
    }
  }
  return new { parameterName, values };
}
}

MethodCallExpression对象将属性名称MyMember存储在Arguments字段中,值存储在Object字段中。

MethodCallExpression对象将属性名称MyMember及其值存储在字段Arguments中。

如您所见,这三个示例产生相同的结果。

但是,我不高兴必须以不同的方式处理每个集合类型。

如何概括这一点,并从MethodCallExpression的相同字段获得属性名称和值?

使用Lambda - . contains从表达式中获取集合的值

这将完成:

public static dynamic GetCalledMemberAndValidValues<T>(this IQueryable<T> query, Expression<Func<T, bool>> predicate)
{
    var methodCall = predicate.Body as MethodCallExpression;
    Expression collectionExpression = null;
    MemberExpression memberExpression = null;
    if (methodCall != null && methodCall.Method.Name == "Contains")
    {
        if (methodCall.Method.DeclaringType == typeof(Enumerable))
        {
            collectionExpression = methodCall.Arguments[0];
            memberExpression = methodCall.Arguments[1] as MemberExpression;
        } else {
            collectionExpression = methodCall.Object;
            memberExpression = methodCall.Arguments[0] as MemberExpression;
        }
    }
    if (collectionExpression != null && memberExpression != null)
    {
        var lambda = Expression.Lambda<Func<object>>(collectionExpression, new ParameterExpression[0]);
        var value = lambda.Compile()();
        return new { parameterName = memberExpression.Member.Name, values = value };
    } 
    return null;
}

你不能让它完全通用。这两个方法:
(new[] { "b", "a" }).Contains
samplEnumerable.Contains

实际上正在调用Enumerable.Contains(source, x.Member)

sampleList.Contains(

调用sampleList.Contains(x.Member)

注意参数的数量和顺序。虽然它们在编写代码时看起来是一样的,但你实际上引用了不同的方法。这将检查哪个包含被调用的方法,然后找出哪个参数是集合,哪个参数是成员表达式。