如何使用我自己的方法或编写DbFunction进行EF Core查询(EF Core 3.0)

时间:2019-10-17 09:11:16

标签: c# entity-framework linq entity-framework-core

我以前进行了以下设置:

public static bool BlogIsLive(BlogPost b)
{
    return b.Status == (int)ItemStatus.Active && b.PublishDate < DateTime.Now ;
}

/// Database query

var blogs = (from b in db.BlogPost 
             where BlogIsLive(b) // <--- super useful, used in multiple places
             select b
             ).ToList()

但是更新到EF Core 3.0之后,它会引发以下错误

/// The LINQ expression ... could not be translated. Either rewrite the query in a form 
/// that can be translated, or switch to client evaluation explicitly by inserting a 
/// call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().

我了解这是EF Core 3.0重大更改的一部分

现在,我必须在BlogsIsLive()之前的所有位置手动编写查询。

var blogs = from b in db.BlogPost 
            where b.Status == (int)ItemStatus.Active  //<--- Annoying repetition of code
            && b.PublishDate < DateTime.Now           //<---
            select b

这很烦人。我无法写出插入其中的方法吗?

我知道EF具有DbFunctions,例如,它可以简化比较Date值的过程,因此我看不出为什么无法编写自己的类似{{1 }},Intstring

类似:

bool

我已经尝试了以上几种方法,但是没有运气。

谢谢。

2 个答案:

答案 0 :(得分:2)

原始代码有一个严重的错误,该错误也可能会在任何非核心版本的EF中引发-它是一个本地函数,不能转换为SQL。 Where接受表达式作为参数,而不是函数。无论如何,您不需要该功能。

LINQ与IQueryable和表达式一起使用。每个运算符采用一个IQueryable,然后返回另一个。 WhereSelect就是这样工作的。这意味着您可以创建自己的函数来添加所需的Where条件:

public static IQueryable<BlogPost> WhereActive(this IQueryable<BlogPost> query)
{
    return query.Where(b=>b.Status == (int)ItemStatus.Active && b.PublishDate < DateTime.Now);
}

并与任何IQueryable<BlogPost>一起使用,例如:

var finalQuery = query.WhereActive();
var posts=finalQuery.ToList();

另一个更麻烦的选择是在代码中构造Expression<Func<>>调用,并将其传递给Where-本质上是动态创建WHERE条件。在这种情况下,虽然不需要。

EF Core 1.0添加了一个非常不幸的功能(更像what-were-they-thinking!这种功能),客户端评估。如果某些内容无法翻译,只需将所有内容加载到内存中,然后尝试过滤内容,而无需使用索引,执行计划,匹配算法,RAM和CPU在数据库服务器中找到的好处。

如果一次仅由一个客户端一次加载仅100行,这可能不会引起注意,这对于具有少量数据和并发用户的任何应用程序都是一种杀手f。

在Web应用程序中,这将转换为更多服务器以处理相同的流量。

这就是为什么在2008年EF 1.0推出时取消了客户端评估的原因。

答案 1 :(得分:1)

您可以使用已经具有该过滤器的DbSet,而不是使用db.BlogPost作为查询的基础。

DbSet<BlogPost> _allBlogs {get;set;}

IQueryable<BlogPost> ActiveBlogs { get => _allBlogs.Where(b=> b.Status == (int)ItemStatus.Active); }

var blogs = from b in db.ActiveBlogs
        select b