如何通过通用字段

时间:2016-05-16 10:33:04

标签: c# linq-to-sql

我试图制作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.

甚至可以将其编译成可重用的函数吗?帮助?!

1 个答案:

答案 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();
}