Entity Framework 6 投影生成 SQL 等价于“Select *”并且不生成 WHERE 子句

Posted

技术标签:

【中文标题】Entity Framework 6 投影生成 SQL 等价于“Select *”并且不生成 WHERE 子句【英文标题】:Entity framework 6 projection generates SQL equivalent to "Select *" and does not produce WHERE clause 【发布时间】:2016-03-12 04:14:55 【问题描述】:

我正在使用实体框架 6 和 MSSQL 服务器数据库维护一个 ASP.NET WebAPI2 应用程序。 IoC 容器是 Castle Windsor。我在我的存储库中有一个方法,用于从 DB 获取用户的一些详细信息。由于我不需要每一列,我想我会使用投影。问题是生成的 SQL 选择了我表中的所有列。 这是 DbContext

public partial class SecurityContext : DbContext

    public SecurityContext()
        : base("name=SecurityContext")
    
    

    public virtual DbSet<User> secUsers  get; set; 

这里是在存储库中声明/初始化上下文的地方

public class BaseRepository<T> : IRepository<T> where T : class

    protected DbContext context;

    public BaseRepository()
    
        context = new SecurityContext();
    

    public BaseRepository(DbContext context)
    
        this.context = context;
     
    //elided

这是存储库中的方法

public User FindUserForLoginVerification(string name)
         
    var loginInfo = context.Set<User>()
        .Where(c => c.LoginName == name)
        .Select(c => new 
         
            LoginName = c.LoginName, 
            Password = c.HashedPassword, 
            Salt = c.PasswordHashSalt 
        )
        .SingleOrDefault();

    return new User()  
        LoginName = loginInfo.LoginName, 
        HashedPassword = loginInfo.Password, 
        PasswordHashSalt = loginInfo.Salt                
    ;

这是输出 SQL。

SELECT 
[Extent1].[UserId] AS [UserId], 
[Extent1].[CreatedByUserId] AS [CreatedByUserId], 
[Extent1].[Comment] AS [Comment], 
[Extent1].[CreatedDate] AS [CreatedDate], 
[Extent1].[DefaultCulture] AS [DefaultCulture], 
[Extent1].[EmailAddress] AS [EmailAddress], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[IsDeleted] AS [IsDeleted], 
[Extent1].[IsExcludedFromPasswordPolicy] AS [IsExcludedFromPasswordPolicy], 
[Extent1].[IsChangePassword] AS [IsChangePassword], 
[Extent1].[IsLocked] AS [IsLocked], 
[Extent1].[LastName] AS [LastName], 
[Extent1].[LastPasswordChangeDate] AS [LastPasswordChangeDate], 
[Extent1].[LoginName] AS [LoginName], 
[Extent1].[NumberOfFailedLoginAttempts] AS [NumberOfFailedLoginAttempts], 
[Extent1].[PasswordHash] AS [PasswordHash], 
[Extent1].[PasswordHashSalt] AS [PasswordHashSalt]
[Extent1].[UpdatedDate] AS [UpdatedDate]
FROM [dbo].[User] AS [Extent1]

我想我做错了什么,但我不知道是什么。任何想法将不胜感激。

编辑:我刚刚注意到一些奇怪的事情——在生成的 SQL 中没有 WHERE 子句,这意味着所有行都是从数据库中选择的,带到客户端,然后在那里过滤。 编辑 2:使用 LINQ 查询语法生成相同的 SQL。 编辑 3:在编写单元测试后,我手动实例化存储库和服务(而不是将其留给 CastleWindsor),运行测试时生成的 SQL 具有 WHERE 子句。

【问题讨论】:

在您的上下文中,集合是如何声明的?他们有public virtual 还是只有public 为什么是context.Set&lt;User&gt;() 而不是context.secUsers() @GregoryHouseMD 因为在存储库中,上下文作为 DbContext 传递。在回答您的评论之前,我已尝试将其转换为 SecurityContext,但没有产生任何差异。 你试过var loginInfo = context.Set&lt;User&gt;() .Where(c =&gt; c.LoginName == name).ToList().Select...吗? 我注意到通用存储库的行为相同。例如,如果您有类似 context.Set&lt;T&gt;().Where(condition).Select(user =&gt; user.Name) 的代码,则不会生成 where 子句或投影。 【参考方案1】:

如果您的context 是从Set&lt;T&gt; 方法返回IEnumerable&lt;T&gt;(而不是IQueryable&lt;T&gt;)的东西,那么这就是您的问题,因为表达式:

context.Set<User>
.Where(...)
.Select(...)
.SingleOrDefault()

...将整个表读入内存,然后应用Where 子句和投影(Select)。所以,你期待SELECT * FROM table的行为。

Set&lt;T&gt;DbContext 类实现返回一个 DbSet&lt;T&gt;,它确实实现了 IQueryable&lt;T&gt;,这样就可以了。但由于看起来您有一个自定义存储库实现,我怀疑幕后可能发生的其他事情......

【讨论】:

以上是关于Entity Framework 6 投影生成 SQL 等价于“Select *”并且不生成 WHERE 子句的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Entity Framework 6 会为简单的查找生成复杂的 SQL 查询?

Entity Framework 6 Code first:如何使用“循环”关系和存储生成的列来播种数据?

Entity Framework 6 和 mysql TableDetails 强类型异常

如何转换 Entity Framework 6 来为我的模型生成 FluentAPI 定义,而不是数据注释? (我在 VS2015 中使用 MySQL)

从 Entity Framework 5 升级到 6

Entity Framework 6.0 - 删除时的奇怪行为