我想生成一个查询,该查询使用导航属性将内部联接用于一对一关系。这是针对我无法更改的现有数据库。
这是我的实体的样子:
[Table("Info")]
public class Info
{
[Key]
public int InfoId { get; set; }
public string Name { get; set; }
public virtual MoreInfo MoreInfo { get; set; }
}
[Table("MoreInfo")]
public class MoreInfo
{
[Key]
public int InfoId { get; set; }
public string OtherName { get; set; }
public int OtherNumber { get; set; }
}
这是我的查询:
var x = await context.Infos
.Select(info => new
{
info.InfoId,
info.Name,
info.MoreInfo.OtherName,
info.MoreInfo.OtherNumber
})
.ToListAsync();
这将产生此SQL(使用左连接):
SELECT [info].[InfoId], [info].[Name], [info.MoreInfo].[OtherName], [info.MoreInfo].[OtherNumber]
FROM [Info] AS [info]
LEFT JOIN [MoreInfo] AS [info.MoreInfo] ON [info].[InfoId] = [info.MoreInfo].[InfoId]
如果Info中的记录不在MoreInfo中,这将导致错误,这就是为什么我要进行内部联接。
我在OnModelCreating中尝试了各种选项,例如
modelBuilder.Entity<Info>()
.HasOne(p => p.MoreInfo)
.WithOne()
.IsRequired();
或将导航属性设置为“必需”
[Table("Info")]
public class Info
{
[Key]
public int InfoId { get; set; }
public string Name { get; set; }
[Required]
public virtual MoreInfo MoreInfo { get; set; }
}
或在查询中使用包含
var x = await context.Infos
.Include(info => info.MoreInfo)
.Select(info => new
{
info.InfoId,
info.Name,
info.MoreInfo.OtherName,
info.MoreInfo.OtherNumber
})
.ToListAsync();
但这些都没有更改联接。
我能够通过使用join函数而不是使用navigation属性来获得内部联接,但是我想尽可能避免这种情况。语法很丑陋,需要我将MoreInfos添加到上下文中。如果可能的话,我想使用导航属性创建一个内部联接。
var x = await context.Infos
.Join(context.MoreInfos, info => info.InfoId, moreInfo => moreInfo.InfoId, (info, moreInfo) =>
new
{
info.InfoId,
info.Name,
moreInfo.OtherName,
moreInfo.OtherNumber
})
.ToListAsync();
产生正确的SQL
SELECT [info].[InfoId], [info].[Name], [moreInfo].[OtherName], [moreInfo].[OtherNumber]
FROM [Info] AS [info]
INNER JOIN [MoreInfo] AS [moreInfo] ON [info].[InfoId] = [moreInfo].[InfoId]
但是我宁愿使用Navigation属性。
答案 0 :(得分:1)
当前(EF Core 2.1.2)无法使用提供的模型。
从技术上讲,无法实施真正的一对一关系,因此EF Core IsRequired
表示从属实体FK是否可为空。对于像您这样的共享PK关联,哪个始终是正确的。请注意,Info
是主体,而MoreInfo
是依赖者,因此MoreInfo
要求Info
,反之则不然,这就是EF Core在导航时生成左外部联接的原因从委托人到受抚养人。
您可以做的一件事是包括不为空的条件:
.Where(info => info.MoreInfo != null)
在EF6查询中使用(带有一些中间投影)可以使EF6查询转换器使用内部联接。 EF Core仍然不够智能,因此所有这些技巧都不起作用。它使用右侧键IS NOT NULL
过滤器生成左外部联接。这等效于内部联接,并且可能会由SQL查询优化器这样处理。但是还是...
当前获得内部联接的唯一方法(当然是手动联接除外)是从关系的 other 端导航,即从依赖关系导航到 required 委托人。无需从上下文中公开DbSet<MoreInfo>
,Set<TEntity>()
方法可用于访问任何实体集。不过,您需要一个反向导航属性:
public class MoreInfo
{
// ...
public virtual Info Info { get; set; }
}
当然还有更新的流利配置:
modelBuilder.Entity<Info>()
.HasOne(e => e.MoreInfo)
.WithOne(e => e.Info)
.HasForeignKey<MoreInfo>(e => e.InfoId);
现在查询可以重写为:
var x = await context.Set<MoreInfo>()
.Select(moreInfo => new
{
moreInfo.Info.InfoId,
moreInfo.Info.Name,
moreInfo.OtherName,
moreInfo.OtherNumber
})
.ToListAsync();
这将生成所需的内部联接:
SELECT [moreInfo].[InfoId], [moreInfo.Info].[Name], [moreInfo].[OtherName], [moreInfo].[OtherNumber]
FROM [MoreInfo] AS [moreInfo]
INNER JOIN [Info] AS [moreInfo.Info] ON [moreInfo].[InfoId] = [moreInfo.Info].[InfoId]