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

时间:2019-04-26 07:07:14

标签: c# asp.net-core entity-framework-core

我们将关系从必需更改为可选,现在由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);
    });
    ...
}

然后我们执行以下操作:

MyContext.First.Include(p => p.Second).ThenInclude(p => p.Third);

由于该关系是可选的,因此这将产生一个左外部联接。然后,这当然会绕过查询过滤器。有没有办法让它包括一个INNER JOIN呢?

当前,这可以通过在以后的位置添加更多条件来解决:

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

但这是不希望的,因为在某些情况下,_tenant为空,然后将提供错误的数据。

我知道我可以翻转它并继续前进

MyContext.Third.Include() ...

但这也是不可取的,因为在这种情况下,“第一”具有许多其他相关数据,并且我不想无休止地将Include()。ThenInclude()链接到荒谬之处。

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

1 个答案:

答案 0 :(得分:2)

  

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

不能。而你不应该。因为尽管inner join可能会解决您的特殊情况,但通常它会过滤所有具有null FK(例如First.SecondId == null)的从属实体,这与 optional 的整个概念背道而驰。 em>关系。

我看到的问题是您似乎正在尝试使用Include进行过滤。从概念上讲Include是什么意思-对于查询返回的每个实体,还包括相关数据。既不打算过滤实体也不过滤相关数据。

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

实际的问题是EF Core Global Query Filters不支持criteria based on navigation properties。这就是为什么人们在这种情况下破坏规范化(引入冗余)并将TenantId属性(列)放在每个实体(表)中,从而允许他们为每个实体设置全局过滤器。

话虽如此,显式查询过滤器(Where)是目前唯一的选择。

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

但这是不可取的,因为在某些情况下_tenant为空,然后将提供错误的数据。

好吧,例如,您只需要考虑可选关系的正确条件

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

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