如何使用表达式使用子项级别?
How to use subItem level using Expression?
我有下一个模型:
public class MaterialCatalog {
public int MaterialCatalogID { get; set; }
public int AccountID { get; set; }
public string MaterialName { get; set; }
public ICollection<MaterialCategory> Categories { get; set; }
}
public class MaterialCategory {
public int MaterialCatalogID { get; set; }
public int UserCategoryID { get; set; }
}
我需要为此表达式创建 IQueryable
通用外部方法:
var items = from l in ctx.WorkareaTemplates
where l.WorkareaTemplateCategories.Any(mc => mc.UserCategoryID == req.Category;
这是我试过的。我得到一个错误:
The double operator Equal is not defined for the types
'System.Collections.Generic.ICollection`1[MaterialCategory]' and 'System.Int32'.
如何转到子属性以使用 MaterialCategory
?
public static IQueryable<TSource> FilterByUserCategoryId<TSource>(this IQueryable<TSource> source, object searchValue) {
if(typeof(TSource).GetProperties().Any(x => x.Name == "Categories")) {
var parameter = Expression.Parameter(typeof(TSource), "e");
var prop = Expression.Property(parameter, "Categories");
var value = Expression.Constant(searchValue);
var body = Expression.Equal(prop, value);
var lambda = Expression.Lambda<Func<TSource, bool>>(body, parameter);
var overload = typeof(Queryable).GetMethods().First(x => x.Name == "Any" && x.GetParameters().Length == 2);
var orderByGeneric = overload.MakeGenericMethod(typeof(TSource), prop.Type);
var result = orderByGeneric.Invoke(null, new object[] { source, lambda });
return (IQueryable<TSource>)result;
}
return source;
}
所以,你要做的操作基本上是这样的:
var items = ctx.WorkareaTemplates
.Where(x => x.Categories.Any(c => c.UserCategoryID == req.Category));
您缺少的最重要的部分是您需要执行 2 个查询操作:
IQueryable<MaterialCatalog>.Where
(过滤 MaterialCatalog
)
IEnumerable<MaterialCategory>.Any
(查看 MaterialCategory
上的成员资格)。
也就是说,检查这个示例实现(您很可能想针对极端情况和例外情况加强它):
public static IQueryable<TSource> FilterByUserCategoryId<TSource>(this IQueryable<TSource> source, object searchValue) {
var catsType = typeof(TSource).GetProperties().Single(p => p.Name == "Categories").PropertyType; // ICollection<MaterialCategory>
var isIEnum = (Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
var catType = catsType.GetInterfaces().First(isIEnum).GetGenericArguments()[0]; // MaterialCategory
// GOAL:
// source.Where(x => x.Categories.Any(c => c.UserCategoryID == searchValue));
var cParam = Expression.Parameter(catType, "c"); // MaterialCategory c
var idProp = Expression.Property(cParam, "UserCategoryID"); // c.UserCategoryID
var idConst = Expression.Constant(searchValue); // searchValue
var equal = Expression.Equal(idProp, idConst); // c.UserCategoryID == searchValue
var anyPred = Expression.Lambda(equal, new[] { cParam }); // c => c.UserCategoryID == searchValue
// this is questionable... relying on the current API of Enumerable.cs
var anyMeth = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Count() == 2); // Enumerable.Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
var anyMethGeneric = anyMeth.MakeGenericMethod(catType); // Enumerable.Any<MaterialCategory>(this IEnumerable<MaterialCategory> source, Func<MaterialCategory, bool> predicate)
var xParam = Expression.Parameter(typeof(TSource), "x"); // MaterialCatalog x
var catsMember = Expression.Property(xParam, "Categories"); // x.Categories
var anyCall = Expression.Call(null, anyMethGeneric, catsMember, anyPred); // x.Categories.Any(c => c.UserCategoryID == searchValue));
var wherePred = Expression.Lambda(anyCall, new[] { xParam }); // x => x.Categories.Any(c => c.UserCategoryID == searchValue)
// this is even more questionable... relying on the private implementation of Queryable.cs
var whereMeth = typeof(Queryable).GetMethods().First(m => m.Name == "Where"); // QueriableWhere<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
var whereMethGeneric = whereMeth.MakeGenericMethod(typeof(TSource)); // Queriable.Where<MaterialCategory>(this IQueryable<MaterialCategory> source, Expression<Func<MaterialCategory, bool>> predicate)
var result = whereMethGeneric.Invoke(null, new object[] { source, wherePred }); // source.Where(x => x.Categories.Any(c => c.UserCategoryID == searchValue))
return (IQueryable<TSource>)result;
}
我有下一个模型:
public class MaterialCatalog {
public int MaterialCatalogID { get; set; }
public int AccountID { get; set; }
public string MaterialName { get; set; }
public ICollection<MaterialCategory> Categories { get; set; }
}
public class MaterialCategory {
public int MaterialCatalogID { get; set; }
public int UserCategoryID { get; set; }
}
我需要为此表达式创建 IQueryable
通用外部方法:
var items = from l in ctx.WorkareaTemplates
where l.WorkareaTemplateCategories.Any(mc => mc.UserCategoryID == req.Category;
这是我试过的。我得到一个错误:
The double operator Equal is not defined for the types 'System.Collections.Generic.ICollection`1[MaterialCategory]' and 'System.Int32'.
如何转到子属性以使用 MaterialCategory
?
public static IQueryable<TSource> FilterByUserCategoryId<TSource>(this IQueryable<TSource> source, object searchValue) {
if(typeof(TSource).GetProperties().Any(x => x.Name == "Categories")) {
var parameter = Expression.Parameter(typeof(TSource), "e");
var prop = Expression.Property(parameter, "Categories");
var value = Expression.Constant(searchValue);
var body = Expression.Equal(prop, value);
var lambda = Expression.Lambda<Func<TSource, bool>>(body, parameter);
var overload = typeof(Queryable).GetMethods().First(x => x.Name == "Any" && x.GetParameters().Length == 2);
var orderByGeneric = overload.MakeGenericMethod(typeof(TSource), prop.Type);
var result = orderByGeneric.Invoke(null, new object[] { source, lambda });
return (IQueryable<TSource>)result;
}
return source;
}
所以,你要做的操作基本上是这样的:
var items = ctx.WorkareaTemplates
.Where(x => x.Categories.Any(c => c.UserCategoryID == req.Category));
您缺少的最重要的部分是您需要执行 2 个查询操作:
IQueryable<MaterialCatalog>.Where
(过滤MaterialCatalog
)IEnumerable<MaterialCategory>.Any
(查看MaterialCategory
上的成员资格)。
也就是说,检查这个示例实现(您很可能想针对极端情况和例外情况加强它):
public static IQueryable<TSource> FilterByUserCategoryId<TSource>(this IQueryable<TSource> source, object searchValue) {
var catsType = typeof(TSource).GetProperties().Single(p => p.Name == "Categories").PropertyType; // ICollection<MaterialCategory>
var isIEnum = (Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
var catType = catsType.GetInterfaces().First(isIEnum).GetGenericArguments()[0]; // MaterialCategory
// GOAL:
// source.Where(x => x.Categories.Any(c => c.UserCategoryID == searchValue));
var cParam = Expression.Parameter(catType, "c"); // MaterialCategory c
var idProp = Expression.Property(cParam, "UserCategoryID"); // c.UserCategoryID
var idConst = Expression.Constant(searchValue); // searchValue
var equal = Expression.Equal(idProp, idConst); // c.UserCategoryID == searchValue
var anyPred = Expression.Lambda(equal, new[] { cParam }); // c => c.UserCategoryID == searchValue
// this is questionable... relying on the current API of Enumerable.cs
var anyMeth = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Count() == 2); // Enumerable.Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
var anyMethGeneric = anyMeth.MakeGenericMethod(catType); // Enumerable.Any<MaterialCategory>(this IEnumerable<MaterialCategory> source, Func<MaterialCategory, bool> predicate)
var xParam = Expression.Parameter(typeof(TSource), "x"); // MaterialCatalog x
var catsMember = Expression.Property(xParam, "Categories"); // x.Categories
var anyCall = Expression.Call(null, anyMethGeneric, catsMember, anyPred); // x.Categories.Any(c => c.UserCategoryID == searchValue));
var wherePred = Expression.Lambda(anyCall, new[] { xParam }); // x => x.Categories.Any(c => c.UserCategoryID == searchValue)
// this is even more questionable... relying on the private implementation of Queryable.cs
var whereMeth = typeof(Queryable).GetMethods().First(m => m.Name == "Where"); // QueriableWhere<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
var whereMethGeneric = whereMeth.MakeGenericMethod(typeof(TSource)); // Queriable.Where<MaterialCategory>(this IQueryable<MaterialCategory> source, Expression<Func<MaterialCategory, bool>> predicate)
var result = whereMethGeneric.Invoke(null, new object[] { source, wherePred }); // source.Where(x => x.Categories.Any(c => c.UserCategoryID == searchValue))
return (IQueryable<TSource>)result;
}