我正在查看执行简单选择查询时生成的SQL。我首先使用来自nuget的示例博客上下文代码。
如果运行以下命令:
BlogContext _context = new BlogContext();
var comments = _context.Comments.Select(c => new CommentReadOnly {Author = c.Author});
var count = comments.Count();
生成以下SQL:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Comments] AS [Extent1]
) AS [GroupBy1]
在预期的SQL中执行计数。
但是,如果我将代码更改为如下所示:
BlogContext _context = new BlogContext();
var comments = _context.Comments.Select(c => new CommentReadOnly {Author = c.Author});
var count = comments.Count();
private CommentReadOnly ToCommentReadOnly(Comment comment)
{
return new CommentReadOnly
{
Author = comment.Author,
};
}
生成以下SQL:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[PostID] AS [PostID],
[Extent1].[Text] AS [Text],
[Extent1].[Author] AS [Author]
FROM [dbo].[Comments] AS [Extent1]
在代码中完成count
。
原因(我认为)是因为第一个作为IQueryable
返回,而第二个作为IEnumerable
。
是否可以在不执行SQL的情况下将第二个查询作为IQueryable返回?
我问的原因是我正在创建一个通用的存储库层,可以查询我的实体并将它们转换为所需的类型(在上面的示例中,注释可能有几个不同的'readonly'对象)。我不希望SQL执行得如此早,可以在不同的情况下进行分页或进行其他过滤。
答案 0 :(得分:0)
我认为这两个查询没有任何区别。但是,我想您希望将IQueryable对象返回给客户端,以便客户端可以执行进一步的过滤并从那里获取计数。
您可以直接返回对象而不进行选择,让客户端完成剩下的工作。
return _context.Comments
客户端可以对此IQueryable对象执行其他过滤
答案 1 :(得分:0)
我认为在你的第二个查询中你执行函数ToCommentReadOnly()所以这不能完全在SQL中完成,你最终得到一个Linq To Objects(IEnumerable)。
但是您声明要从存储库中返回IQueryable。这不是推荐的做法!访问数据的代码应隐藏在您的存储库中,否则您将遇到问题。
例如,假设您的存储库(封装了您的ObjectContext)超出了范围,之后您尝试枚举存储库为您提供的IQueryable结果。这将引发错误,因为IQueryable不能再执行了。
如果从存储库中公开IQueryable,您可以为存储库的最终用户提供构建自己的查询所需的所有自由,这是您希望通过添加存储库来避免的事情!
因此从存储库返回IEnumerable是一件好事:)