我正在使用动态Linq进行通用搜索。 我有ID列表:
List<int> idList = new List<int> { 1, 5, 6};
在普通的Linq,我会写:
q = q.Where(a => idList.Contains(a.MyId));
但现在我必须使用System.Linq.Dynamic
,因为我事先并不知道该列的名称。
string someId = "CustomId";
q = q.Where("@0"+ ".Contains(" + someId + ")", idList.ToArray());
但这会产生错误:
&#34;没有适用的方法&#39;包含&#39;存在于类型&#39; Int32&#39;&#34;
中
我怎样才能做到这一点?
是否有一些扩展库为Contains
Linq或其他方式实现dynamic
。
答案 0 :(得分:4)
您可以使用expressions
执行此动态查询,尝试类似这样的示例:
导入这些名称空间:
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
试试这个:
// a reference parameter
var x = Expression.Parameter(typeof (YourType), "x");
// contains method
var containsMethod = typeof (string).GetMethod("Contains", new[] {typeof (string)});
// reference a field
var fieldExpression = Expression.Property(instance, "PropertyName");
// your value
var valueExpression = Expression.Constant(yourId);
// call the contains from a property and apply the value
var containsValueExpression = Expression.Call(fieldExpression, containsMethod, valueExpression);
// create your final lambda Expression
var filterLambda = Expression.Lambda<Func<YourType, bool>>(containsValueExpression, x);
// apply on your query
q = q.Where(finalLambda);
Obs:确保您的媒体资源有一个名为contains
的方法。
答案 1 :(得分:4)
您可以编写类似这样的内容来动态构建查询函数:
public static Func<ObjT, bool> PropertyCheck<ObjT, PropT>(string propertyName, Expression<Func<PropT, bool>> predicate)
{
var paramExpr = Expression.Parameter(typeof(ObjT));
var propExpr = Expression.Property(paramExpr, propertyName);
return Expression.Lambda<Func<ObjT, bool>>(Expression.Invoke(predicate, propExpr), paramExpr).Compile();
}
然后,它可以像这样使用:
foos.Where(PropertyCheck<Foo, int>("MyId", x => idList.Contains(x)));
当然,您也可以提供自己的Where
扩展方法,同时完成所有这些操作:
public static IEnumerable<T> Where<T, PropT>(this IEnumerable<T> self, string propertyName, Expression<Func<PropT, bool>> predicate)
{
var paramExpr = Expression.Parameter(typeof(T));
var propExpr = Expression.Property(paramExpr, propertyName);
return self.Where<T>(Expression.Lambda<Func<T, bool>>(Expression.Invoke(predicate, propExpr), paramExpr).Compile());
}
foos.Where<Foo, int>("MyId", x => idList.Contains(x));
答案 2 :(得分:1)
如果查看Dynamic LINQ的源代码,那么您可以看到在许多情况下解析依赖于变量predefinedTypes
。
在您的情况下,您需要像这样更改此变量
static readonly Type[] predefinedTypes = {
....
,typeof(List<int>)
};
之后,下一个代码将起作用
List<int> idList = new List<int> { 1, 5, 6};
....
string someId = "CustomId";
q = q.Where("@0.Contains(" + someId + ")", idList);
答案 3 :(得分:0)
另一种修饰此猫的方法是将包含转换为OR。
someArray.Constains(someField)相当于:
someField == someArray [0]或someField == someArray [1]等等。
这不太理想,但如果阵列很小就可以工作。
答案 4 :(得分:0)
@Felipe Oriani在其90%的回答中使用了string.Contains
方法和yourId
单个值,但询问的是:
q = q.Where(a => idList.Contains(a.MyId));
是对a
的成员(属性)访问权限。
这是经过测试的最终扩展方法:
/// <summary>
/// Creates lambda expression predicate: (TEntity entity) => collection.Contains(entity.property)
/// </summary>
public static Expression<Func<TEntity, bool>> ContainsExpression<TEntity, TProperty, TCollection>(
this TCollection collection,
Expression<Func<TEntity, TProperty>> property
)
where TCollection : ICollection<TProperty>
{
// contains method
MethodInfo containsMethod = typeof(TCollection).GetMethod(nameof(collection.Contains), new[] { typeof(TProperty) });
// your value
ConstantExpression collectionInstanceExpression = Expression.Constant(collection);
// call the contains from a property and apply the value
var containsValueExpression = Expression.Call(collectionInstanceExpression, containsMethod, property.Body);
// create your final lambda Expression
Expression<Func<TEntity, bool>> result = Expression.Lambda<Func<TEntity, bool>>(containsValueExpression, property.Parameters[0]);
return result;
}
示例:
List<int> idList = new List<int> { 1, 5, 6 };
Expression<Func<MyEntity,int>> idExpression = entity => entity.Id;
var contains = idList.ContainsExpression(idExpression)
IQueryable<MyEntity> q = DbContext.Set<MyEntity>().Where(contains);