使用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
的相同字段获得属性名称和值?
这将完成:
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)
。
注意参数的数量和顺序。虽然它们在编写代码时看起来是一样的,但你实际上引用了不同的方法。这将检查哪个包含被调用的方法,然后找出哪个参数是集合,哪个参数是成员表达式。