C# 在可选关系上强制从EF Cores Include()进行内部联接

C# 在可选关系上强制从EF Cores Include()进行内部联接,c#,asp.net-core,entity-framework-core,C#,Asp.net Core,Entity Framework Core,我们将一个关系从required更改为optional,现在由EF-Core的Include()生成的SQL执行左外部连接而不是内部连接。问题是,这些可选实体上有必要的查询过滤器 假设我们有以下几点: public class First { public int? SecondId { get; set; } } public class Second { public First First { get; set; } public int ThirdId { g

我们将一个关系从required更改为optional,现在由EF-Core的Include()生成的SQL执行左外部连接而不是内部连接。问题是,这些可选实体上有必要的查询过滤器

假设我们有以下几点:

public class First 
{
    public int? SecondId { get; set; }
}
public class Second 
{
    public First First { get; set; }
    public int ThirdId { get; set; }
}
public class Third
{
    public Second Second { get; set; }
    public string Tenant { get; set; }
}

public class MyContext : DbContext 
{
    protected readonly string _tenant;
    ...
    modelBuilder.Entity<Third>(p =>
    {
        p.HasQueryFilter(x => Tenant == _tenant);
    });
    ...
}
这将产生一个左外部联接,因为该关系是可选的。这当然会绕过查询过滤器。有没有办法让它包含一个内部连接

目前,可通过向以下位置添加更多条件来解决此问题:

.Where(p => p.Second.Third.Tenant == _tenant);
但这是不可取的,因为在某些边缘情况下_租户为null,然后会给出错误的数据

我知道我可以把它翻过来,然后继续

MyContext.Third.Include() ...
但这也是不可取的,因为在这个场景中,第一个有很多其他相关数据,我不想无休止地将Include()链接起来,然后将Include()链接到荒谬的地步

我可以强制使用可选实体进行内部连接吗?或者我必须为此手动编写SQL吗

我可以强制使用可选实体进行内部连接吗

你不能。你不应该。因为虽然
内部连接
可能会解决您的特殊情况,但通常它会过滤所有具有
null
FK(例如
First.SecondId==null
)的依赖实体,这与可选关系的整个概念背道而驰

我看到的问题是,您似乎试图使用
Include
进行过滤。按idea
Include
就是它所说的-对于查询返回的每个实体,也包括相关数据。它不打算过滤实体或相关数据

因此,您需要的是一个查询过滤器

实际问题是EF核心不支持。这就是为什么在这种情况下,人们会打破规范化(引入冗余),将
TenantId
属性(列)放在每个实体(表)中,这允许他们为每个实体设置全局过滤器

话虽如此,显式查询过滤器(
其中
)是当前唯一的选项

但这是不可取的,因为在某些边缘情况下_租户为null,然后会给出错误的数据

例如,您只需要考虑可选关系的正确标准

.Where(p => p.SecondId == null || p.Second.Third.Tenant == _tenant);

但这实际上显示了每个实体上没有
租户的问题-当
First.SecondId==null
时,您无法判断哪个
租户拥有
First

在这一行
,其中(p=>p.Second.Third.Tenant==\u Tenant)可以将其更改为
。其中(p=>\u tenant!=null?\u tenant.Contain(p.Second.Third.tenant):p.Second.Third.tenant==p.Second.Third.tenant)它能解决您的问题吗?因为我正在尝试“复制”一个内部联接,它不是:。其中(p=>p.SecondId!=null&&p.Second.Third.Tenant==\u Tenant)?谢谢你的详细回答。我猜这里最正确的做法是将租户添加到缺少租户的实体中。当然,如果您真的想模拟
内部连接
,并过滤掉
SecondId==null
的记录。
   .Where(p => p.Second.Third.Tenant == _tenant);
.Where(p => p.SecondId == null || p.Second.Third.Tenant == _tenant);