使用Func作为LinqToEntities的参数

时间:2017-06-15 10:51:05

标签: c# performance linq entity-framework-6 linq-to-entities

我有一个Linq-Query来获取我的EF数据。我的查询连接4个表并选择结果为非规范化类型。我经常需要这个查询,但使用不同的谓词。 List-ExtensionMethods(例如.Where()正在使用Func<T,bool>作为参数,我想这样做 - 但我找不到一种方法来访问我的方法中的谓词。

public DenormalizedType GetData(Func<Thing, bool> predicate)
{
    using (var dbContext = new MyDbContext())
    { 
        var myData = (from some in dbContext.Thing 
        join other in dbContext.OtherThing
        on some.OtherId equals other.Id
        // => HowToWhere ???
        select new DenormalizedType()
        {
            SomeEntry = some.Entry
            SomeOtherId = some.OtherId
            OtherValue = other.Value
        }).ToList();
    }
}

关于这个问题我有3个问题。

首先(显然):如何调用我的谓词来使用动态where子句?

第二:如果我的初步想法不起作用(因为teovankots回答表明,我的方法对LinqToEntities无效)是否有可能,只能制作一个连接方法?

第三:将结果返回到另一个软件组件的最佳表现方法是什么?

3 个答案:

答案 0 :(得分:3)

EF查询提供程序需要将LINQ查询表达式树转换为SQL,这在传递Func<...>(更常见的是,调用表达式,如委托,未知方法等)时是不可能的)。

很快,使用Func<...>类型参数无法提出要求。

使用Queryable方法时,首先要考虑的是在Expression<Func<...>>方法中使用Func<..>时使用Enumerable。所以改变你的方法论点如下:

public DenormalizedType GetData(Expression<Func<Thing, bool>> predicate)

不幸的是,开箱即用不支持在LINQ查询语法中使用提供的表达式。为此,您需要一些表达式树处理库。

问题和可能的解决方案在LINQKit包页面中进行了解释。该软件包提供的解决方案是通过AsExpandableInvoke自定义扩展方法。

安装nuget package,添加

using LinqKit;

到源代码文件,现在你可以使用这样的东西来实现目标:

public DenormalizedType GetData(Expression<Func<Thing, bool>> predicate)
{
    using (var dbContext = new MyDbContext())
    { 
        var myData = (from some in dbContext.Thing.AsExpandable() // <= 
        join other in dbContext.OtherThing
        on some.OtherId equals other.Id
        where predicate.Invoke(some) // <=
        select new DenormalizedType()
        {
            SomeEntry = some.Entry
            SomeOtherId = some.OtherId
            OtherValue = other.Value
        }).ToList();
    }
}

答案 1 :(得分:1)

至少我找到了一种方法来解决我的问题 - 但我非常感谢提示,或者让它变得更好的方法,因为我无法想象这是圣杯,甚至是接近......

然而,第一步是我的加入,我作为IQueryable返回。重要提示:此处不使用,因为否则在使用IQueryable时将丢弃dbContext,这不是很好:

private static MyDbContext _dbContext;
private static IQueryable<DenormalizedType> SelectType()
{
    _dbContext = new MyDbContext();
    var myData = (from some in dbContext.Thing 
        join other in dbContext.OtherThing
        on some.OtherId equals other.Id
        select new DenormalizedType()
        {
            SomeEntry = some.Entry
            SomeOtherId = some.OtherId
            OtherValue = other.Value
        };
    return myData;
}

我今天学到了很多东西。例如:IEnumerable和IQueryable都有一个扩展方法.Where()。但只有IEnumerable.Where()有一个Func<T,bool>作为参数。 IQueryable的Expression<Func<T,bool>>需要Where()。如果我想要执行我的查询,我需要使用所有条件,我需要使用IQueryable类型,只要我的所有人都没有执行。所以我需要仔细研究Expression-Type。我不明白所有这些实际上做了什么,但它确实有效;)

我要做的第一件事就是写我的Where-Methods ..在我读完这篇文章之后,这很简单:Entity Framework Filter "Expression<Func<T, bool>>"。方法如下所示:

public static IQueryable<DenormalizedType> SelectWhereCriteria(IQueryable<DenormalizedType> data, Expression<Func<DenormalizedType, bool>> predicate)
{
    return data.Where(predicate);
}

Expression本身有点复杂,因为我有一个Selection-Enum应该选择指定的过滤器。表达式如下:

Expression<Func<DenormalizedType, bool>> FilterBySelection(Selection selection)
{
    switch(selection)
    {
        case Selection.Active:
            return x => x.IsActive == true;
        case Selection.InActive:
            return x => x.IsActive == false;
        case Selection.SomeOtherSelection:
            return x => x.SomeOther == "Criteria"
        default:
            return x => true;
    }
}

这个表达式在我的IQueryable上运行良好:

var selectedQuery = DataHandler.SelectWhereCriteria(query, FilterBySelection(selection));

我现在唯一需要的就是订购。我从MarcGravell找到了一些很酷的东西(天才btw)Dynamic LINQ OrderBy on IEnumerable<T>他在那里发布了一些代码作为答案,你可以使用它来订购OrderBy PropertyName。他的第一段代码采用了IQueryable,通过PropertyName命令它(他也为Descending OrderyBy提供扩展)并返回一个IOrderedQueryable。 ToList() - Operation是我执行的最后一个操作。

还有一件事:别忘了Disb the DbContext:

public static void Dispose()
{
    _dbContext.Dispose();
}

答案 2 :(得分:0)

你可以轻松打电话。安装this package,添加:

public DenormalizedType GetData(Expression<Func<Thing, bool>> predicate)
{
    using (var dbContext = new MyDbContext())
    { 
        var myData = (from some in dbContext.Thing 
        join other in dbContext.OtherThing
        on some.OtherId equals other.Id
        where predicate.Invoke(some) //check this
        select new DenormalizedType()
        {
            SomeEntry = some.Entry
            SomeOtherId = some.OtherId
            OtherValue = other.Value
        }).ToList();
    }
}

你应该知道Func<T1,T2>是一种方法。有了这个签名:

T2 predicate(T1 parameter) { /*...*/ }

您的第二个问题取决于您如何连接您的组件。但是只要你得到DenormalizedType而不是DbEntity,你的例子就可以了。