我试图制作IDictionary< TKey,List< TSource>>我可以用函数来决定键的类。我已经设法用这样的数组构建类(为了示例,函数被简化):
private List<TSource> Filter(TKey key, Func<TSource, TKey> keySelector)
{
IQueryable<TSource> source = GetSource(); // It's there
IQueryable<TSource> filtered = source.Where(
x => keySelector.Invoke(x).Equals(key)
);
return filtered.ToList();
}
但这只适用于数组等,而不适用于linq-to-sql。我明白这可以通过表达来完成,但那些大多数都超出了我的意思。我用Google搜索并提出以下功能:
private List<TSource> Filter(TKey key, Expression<Func<TSource, TKey>> keySelector)
{
IQueryable<TSource> source = GetSource();
Func<TSource, bool> compiledKeyFilter = GetFilter(keySelector);
IEnumerable<TSource> filtered = source.Where(compiledKeyFilter);
return filtered.ToList();
}
private Func<TSource, bool> GetFilter(Expression<Func<TSource, TKey>> expr)
{
if (this.filter == null)
{
var invokedExpr = Expression.Invoke(expr, expr.Parameters.Cast<Expression>());
var lamda = Expression.Lambda<Func<TSource, bool>>(
Expression.Call(expr.Body, typeof(TKey).GetMethod("Equals", new[] { typeof(TKey) }), invokedExpr),
expr.Parameters
);
this.filter = lamda.Compile();
}
return this.filter;
}
这将返回源中的所有行。表达式应该是可重用的,而不仅仅是一次性执行。我从SO(Dynamically generated lookup key for IQueryable)中找到了以下内容,我所做的就是相似。它有效,但我无法将其与编译方法结合起来:
private Expression<Func<TSource, bool>> MakeFilterExpression(TKey key)
{
var param = Expression.Parameter(typeof(TSource));
return Expression.Lambda<Func<TSource, bool>>(
Expression.Equal(
Expression.Invoke(keySelector, param),
Expression.Constant(key)
),
param
);
}
所以我试图想出一个我可以这样使用的课程:
// Inside MyCache there would be something close to this:
class MyCache
{
private Func<TSource, bool> filter;
public MyCache(Expression<Func<TSource, TKey>> func)
{
this.filter = MakeFilter(func);
}
private Func<TSource, bool> MakeFilter(Expression<Func<TSource, TKey>> func)
{
// magic
}
public List<TSource> GetByKey(TKey key)
{
return GetSource().Where(this.filter(key)).ToList();
}
}
// This is my class where I give my func to determine the key in ctor.
var cache = new MyCache<MySource>(x => x.myField);
var list1 = cache.GetByKey(3); // Now I have list to iterate.
var list2 = cache.GetByKey(4); // Here's another list.
甚至可以将其编译成可重用的函数吗?帮助?!
答案 0 :(得分:0)
如果我正确理解了您的问题,那么您正在尝试提供Filter
方法的一个版本,该方法适用于给定Expression<Func<TSource, TKey>>
键选择器的LINQ-to-SQL查询。
您可以使用LINQKit library来帮助您。它允许你从另一个表达式调用一个表达式,然后&#34;展开&#34;结果表达式。
以下是在LINQKit的帮助下编写Filter
方法的方法:
private static List<TSource> Filter<TSource,TKey>(
TKey key,
Expression<Func<TSource, TKey>> keySelector)
{
IQueryable<TSource> source = GetSource<TSource>(); // It's there
Expression<Func<TSource, bool>> predicate = x => keySelector.Invoke(x).Equals(key);
//LINQ-to-SQL cannot translate `Invoke` into SQL. However, after invoking this line
//LINQKit would flatten the expression so that it contains something like
//x => x.Age.Equals(5) (for a keySelector of value x => x.Age and key of value 5)
predicate = predicate.Expand();
IQueryable<TSource> filtered = source.Where(predicate);
return filtered.ToList();
}