多对一关系,不需要的 JOIN

Many to one relationship, unwanted JOIN

我的模型中定义了多对一关系: Project.CreatedBy = ApplicationUser.Id:

class Project{
[Required]
public ApplicationUser CreatedBy {get; set;}
}

有映射:

modelBuilder.Entity<Project>().HasOptional(i => i.CreatedBy).WithOptionalDependent().WillCascadeOnDelete(false);

不幸的是,当我尝试通过以下方式从 DbContext 检索所有用户时:

var users = context.Users;

生成的 SQL 看起来像:

SELECT 
[...]
FROM     [dbo].[AspNetUsers] AS [Extent1]
LEFT OUTER JOIN [dbo].[Projects] AS [Extent4] ON ([Extent4].[CreatedBy_Id] IS NOT NULL) AND ([Extent1].[Id] = [Extent4].[CreatedBy_Id])

所以当某个用户创建了10个项目时,用户实体乘以10倍。这让我无法通过他的用户名查找该用户:

context.Users.Single(u => u.Username == "test")

因为它会给我 Sequence contains more than one element 异常。

你知道如何避免额外的连接吗?

我怀疑它与 modelBuilder 声明有关。我一直在谷歌搜索这个,但从未找到任何解决方案。

任何关于 modelBuilder 和定义与它的关系的材料也将不胜感激。

使用

.FirstOrDefault()

而不是单一。

此方法将 return 第一个对象,如果未找到对象,则返回 null。如果选择了多个元素,单一方法将抛出异常。

您也可以使用

.First()

但是如果没有找到元素,这个方法会抛出异常。

PS 不知道是不是打错了,不过你得用双等号,像这样:

context.Users.Single(u => u.Username == "test")

请记住,通常在这些情况下会创建 LoweredUsername 列并用于比较:

context.Users.Single(u => u.LoweredUsername == providedUsername.ToLower())

这样做的原因:

  • 有人可以输入 John 而不是 john
  • 在 SQL 服务器端建立索引在这种情况下有效

我想在 ApplicationUser 中你有

public virtual ICollection<Project> Projects { get; set; }

请参阅 MSDNturn off lazy loading for all entities

或者 turning off lazy loading for specific navigation properties 使项目 属性 非虚拟

映射...

modelBuilder.Entity<Project>()
            .HasOptional(i => i.CreatedBy)
            .WithOptionalDependent()

...将其表示为 1:1 关联,而不是您想要的 1:n 关联。

所以把映射改成一对多:

modelBuilder.Entity<Project>()
            .HasOptional(p => p.CreatedBy)
            .WithMany()
            .HasForeignKey(p => p.CreatedBy_Id);

我不确定为什么当您查询 User 时 EF 加入 Project,因为关联是可选的),但是适当的 1:n 映射会阻止它。