如何解决查询需要在.ThenInclude之后使用.Where子句进行过滤
我使用以下查询根据需要过滤我的BranchId。
String json = "{...}";
JSONObject object = (JSONObject) JSONValue.parse(jsonString2);
readJSON(object);
上面的表达式创建了以下SQL,我认为var query = await _dbContext.TargetItems
.Include(i => i.BranchTargetItem)
.ThenInclude(i => i.BranchTarget)
.Where(x=> x.BranchTargetItem.Any(x=> x.BranchTarget.Any(x=> x.BranchId.Equals("9417"))))
.ToListAsync(cancellationToken);
导致了这些.Any()
语句
Left Join
但是我想将SELECT *
FROM PERF_TARGET_ITEMS "p"
LEFT JOIN (SELECT *
FROM PERF_BRANCH_TARGET_ITEMS "p0"
LEFT JOIN PERF_BRANCH_TARGETS "p1"
ON "p0".BTI_ID = "p1".BT_BRANCH_TARGET_ITEM_ID) "t"
ON "p".TI_ID = "t".BTI_ITEM_TARGET_ITEM_ID
WHERE EXISTS (SELECT 1
FROM PERF_BRANCH_TARGET_ITEMS "p2"
WHERE ("p".TI_ID = "p2".BTI_ITEM_TARGET_ITEM_ID)
AND EXISTS
(SELECT 1
FROM PERF_BRANCH_TARGETS "p3"
WHERE ("p2".BTI_ID = "p3".BT_BRANCH_TARGET_ITEM_ID)
AND ("p3".BT_BRANCH_ID = N'9417')))
ORDER BY "p".TI_ID,"t".BTI_ID,"t".BT_ID
转换为Left Join
,如下所示,
Inner Join
我想返回 SELECT *
FROM WESTCORE.PERF_BRANCH_TARGET_ITEMS "p"
INNER JOIN WESTCORE.PERF_PERIODS "p0"
ON "p".BTI_ITEM_PERIOD_ID = "p0".P_ID
INNER JOIN WESTCORE.PERF_TARGET_ITEMS "p1"
ON "p".BTI_ITEM_TARGET_ITEM_ID = "p1".TI_ID
LEFT JOIN (SELECT *
FROM WESTCORE.PERF_BRANCH_TARGETS "p2"
WHERE "p2".BT_BRANCH_ID = '9417') "t"
ON "p".BTI_ID = "t".BT_BRANCH_TARGET_ITEM_ID
WHERE ((SELECT COUNT(*)
FROM WESTCORE.PERF_BRANCH_TARGETS "p3"
WHERE ("p".BTI_ID = "p3".BT_BRANCH_TARGET_ITEM_ID)
AND ("p3".BT_BRANCH_ID = '9417')) > 0)
ORDER BY "p".BTI_ID,"p0".P_ID,"p1".TI_ID,"t".BT_ID
并包含其关系实体(TargetItems> BranchTargetItems> BranchTargets)
TargetItems
这是我的关系,
//TargetItem.cs
...
public virtual ICollection<BranchTargetItem> BranchTargetItem { get; set; }
...
//BranchTargetItem.cs
...
public virtual ICollection<BranchTarget> BranchTarget { get; set; }
...
//BranchTarget.cs
...
public virtual BranchTargetItem BranchTargetItem { get; set; }
...
编辑:
我要将此过滤器//TargetItemConfiguration.cs
builder.HasMany(x => x.BranchTargetItem).WithOne(x => x.TargetItem).HasForeignKey(x => x.ItemTargetItemId).IsRequired();
//BranchTargetItemConfiguration.cs
builder.HasMany(x => x.BranchTarget).WithOne(x => x.BranchTargetItem).HasForeignKey(x => x.BranchTargetItemId).IsRequired();
添加到以下查询中,
.ThenInclude( bti=> bti.BranchTarget).Where( bt=> bt.BranchId.Equals("9417"))
编辑:
我已使用以下查询解决了该问题,但我仍在寻找理想的解决方案。
var query = await _dbContext.TargetItems
.Include(ti => ti.BranchTargetItem)
.ThenInclude( bti=> bti.BranchTarget)
.ToListAsync(cancellationToken);
编辑:
我也尝试过这种方法,在这里 var targetItems = await _dbContext.TargetItems
.Include(ti => ti.BranchTargetItem)
.ThenInclude(bti => bti.BranchTarget)
.ToListAsync(cancellationToken);
foreach (TargetItem ti in targetItems)
{
foreach (BranchTargetItem bti in ti.BranchTargetItem)
{
bti.BranchTarget = bti.BranchTarget.Where(bt => bt.BranchId.Equals(branchId)).ToList();
}
}
的TargetItems实体与其自身具有一对多关系,当我在下面的代码中应用时,子节点为空。它只返回第一个节点。
[DataMember] public virtual ICollection<TargetItem> TargetItems { get; set; }
解决方法
您不必担心Any
。 Any
-> EXISTS
。您的问题是Include
,它转换为LEFT OUTER JOIN
。因此,您需要额外的过滤,如下所示:
var query = await _dbContext.TargetItems
.Include(i => i.BranchTargetItem)
.Where(e => e.BranchTargetItem != null) //added filtration
.ThenInclude(i => i.BranchTarget)
.Where(e => e.BranchTarget != null) //added filtration
.Where(x => x.BranchTargetItem.Any(x => x.BranchTarget.Any(x => x.BranchId.Equals("9417"))))
.ToListAsync(cancellationToken);
,
如果您需要INNER JOIN
,则需要使用Join
。可以优化预期的SQL
查询,并将其简化为:
SELECT *
FROM
WESTCORE.PERF_BRANCH_TARGET_ITEMS p
INNER JOIN WESTCORE.PERF_PERIODS p0 ON p.BTI_ITEM_PERIOD_ID = p0.P_ID
INNER JOIN WESTCORE.PERF_TARGET_ITEMS p1 ON p.BTI_ITEM_TARGET_ITEM_ID = p1.TI_ID
INNER JOIN WESTCORE.PERF_BRANCH_TARGETS p2 ON p.BTI_ID = p2.BT_BRANCH_TARGET_ITEM_ID
WHERE
p2.BT_BRANCH_ID = '9417'
ORDER BY
p.BTI_ID,p0.P_ID,p1.TI_ID,p2.BT_BRANCH_TARGET_ITEM_ID
要应用此功能,您可以执行以下操作:
var query = await _dbContext.BranchTargetItem
.Join(_dbContext.TargetItems,bti => bti.BranchId,ti => ti.BranchId,(bti,ti) => new { TargetItems = ti,BranchTargetItem = bti })
.Join(_dbContext.BranchTarget,ti => ti.TargetItems.BranchId,bt => bt.BranchId,BranchTarget = bt })
.Where(x=> x.TargetItems.BranchId.Equals("9417"))
.ToListAsync(cancellationToken);
查询本身需要进行测试并调整为正确的实体。
,如果仅希望与where子句匹配的BranchTarget
,则需要使查询的主要详细信息记录为准,然后包括其父记录;
var query = await _dbContext.BranchTarget
.Where(x => x.BranchId.Equals("9417"))
.Include(t => t.BranchTargetItems)
.ThenInclude(t => t.TargetItems)
.ToListAsync(cancellationToken);
,
原始问题的答案
我使用以下查询根据需要过滤我的BranchId。
java -Xdock:name=Alfabet
但是我想将“左联接”转换为“内联接[...]
要解决您的原始问题,一个简单的解决方案是颠倒您的包含项的顺序:
var query = await _dbContext.TargetItems
.Include(i => i.BranchTargetItem)
.ThenInclude(i => i.BranchTarget)
.Where(x=> x.BranchTargetItem.Any(x=> x.BranchTarget.Any(x=> x.BranchId.Equals("9417"))))
.ToListAsync(cancellationToken);
这将使用var branchTargets = context.BranchTargets
.Include(bt => bt.BranchTargetItem)
.ThenInclude(bti => bti.TargetItem)
.Where(bt => bt.BranchId == "9417")
.ToList();
并返回INNER JOIN
实体,您可以从其中通过BranchTarget
进入TargetItem
实体:
branchTargets[0].BranchTargetItem.TargetItem
如果您不希望查询返回SELECT [p].[BT_BRANCH_TARGET_ITEM_ID],[p].[BT_BRANCH_ID ],[p].[BT_ID],[p0].[BTI_ID],[p0].[BTI_ITEM_TARGET_ITEM_ID],[p1].[TI_ID]
FROM [PERF_BRANCH_TARGETS ] AS [p]
INNER JOIN [PERF_BRANCH_TARGET_ITEMS] AS [p0] ON [p].[BT_ID] = [p0].[BTI_ID]
INNER JOIN [PERF_TARGET_ITEMS] AS [p1] ON [p0].[BTI_ITEM_TARGET_ITEM_ID] = [p1].[TI_ID]
WHERE [p].[BT_BRANCH_ID ] = N'9417'
作为根实体,则可以执行以下操作:
BranchTarget
这里是一个可以正常运行的示例控制台项目,它演示了这种方法:
var targetItems = context.BranchTargets
.Include(bt => bt.BranchTargetItem)
.ThenInclude(bti => bti.TargetItem)
.Where(bt => bt.BranchId == "9417")
.AsEnumerable()
.Select(bt => bt.BranchTargetItem.TargetItem)
.ToList();
有关递归导航属性的后续问题的答案
使用传统方法时,通常有以下选择:
- 如果您知道要查询递归结构的层次数,只需添加其他
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace IssueConsoleTemplate { public class TargetItem { public int TargetItemId { get; set; } public ICollection<BranchTargetItem> BranchTargetItem { get; set; } } public class BranchTargetItem { public int BranchTargetItemId { get; set; } public int ItemTargetItemId { get; set; } public TargetItem TargetItem { get; set; } public ICollection<BranchTarget> BranchTarget { get; set; } } public class BranchTarget { public int BranchTargetId { get; set; } public string BranchId { get; set; } public int BranchTargetItemId { get; set; } public BranchTargetItem BranchTargetItem { get; set; } } public class Context : DbContext { public DbSet<TargetItem> TargetItems { get; set; } public DbSet<BranchTargetItem> BranchTargetItems { get; set; } public DbSet<BranchTarget> BranchTargets { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer( @"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63636238") .UseLoggerFactory( LoggerFactory.Create( b => b .AddConsole() .AddFilter(level => level >= LogLevel.Information))) .EnableSensitiveDataLogging() .EnableDetailedErrors(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<TargetItem>( entity => { entity.ToTable("PERF_TARGET_ITEMS"); entity.Property(e => e.TargetItemId) .HasColumnName("TI_ID"); entity.HasMany(x => x.BranchTargetItem) .WithOne(x => x.TargetItem) .HasForeignKey(x => x.ItemTargetItemId); entity.HasData( new TargetItem {TargetItemId = 1},new TargetItem {TargetItemId = 2},new TargetItem {TargetItemId = 3}); }); modelBuilder.Entity<BranchTargetItem>( entity => { entity.ToTable("PERF_BRANCH_TARGET_ITEMS"); entity.Property(e => e.BranchTargetItemId) .HasColumnName("BTI_ID"); entity.Property(e => e.ItemTargetItemId) .HasColumnName("BTI_ITEM_TARGET_ITEM_ID"); entity.HasMany(x => x.BranchTarget) .WithOne(x => x.BranchTargetItem) .HasForeignKey(x => x.BranchTargetItemId); entity.HasData( new BranchTargetItem {BranchTargetItemId = 11,ItemTargetItemId = 1},new BranchTargetItem {BranchTargetItemId = 22,ItemTargetItemId = 2},new BranchTargetItem {BranchTargetItemId = 33,ItemTargetItemId = 3},new BranchTargetItem {BranchTargetItemId = 41,ItemTargetItemId = 1}); }); modelBuilder.Entity<BranchTarget>( entity => { entity.ToTable("PERF_BRANCH_TARGETS "); entity.Property(e => e.BranchTargetItemId) .HasColumnName("BT_ID"); entity.Property(e => e.BranchId) .HasColumnName("BT_BRANCH_ID "); entity.Property(e => e.BranchTargetId) .HasColumnName("BT_BRANCH_TARGET_ITEM_ID"); entity.HasData( new BranchTarget {BranchTargetId = 100,BranchId = "9417",BranchTargetItemId = 11},new BranchTarget {BranchTargetId = 200,BranchTargetItemId = 41},new BranchTarget {BranchTargetId = 300,BranchId = "1234",BranchTargetItemId = 22}); }); } } internal static class Program { private static void Main() { using var context = new Context(); context.Database.EnsureDeleted(); context.Database.EnsureCreated(); // Your original query: // // var result = context.TargetItems // .Include(ti => ti.BranchTargetItem) // .ThenInclude(bti => bti.BranchTarget) // .Where(ti => ti.BranchTargetItem.Any(bti => bti.BranchTarget.Any(bt => bt.BranchId.Equals("9417")))) // .ToList(); // // Debug.Assert(result.Count == 2); // Debug.Assert(result[0].BranchTargetItem != null); // Debug.Assert(result[0].BranchTargetItem.Count == 2); // Debug.Assert(result[0].BranchTargetItem.First().BranchTargetItemId == 11); // Debug.Assert(result[0].BranchTargetItem.First().BranchTarget != null); // Debug.Assert(result[0].BranchTargetItem.First().BranchTarget.Count == 1); var result = context.BranchTargets .Include(bt => bt.BranchTargetItem) .ThenInclude(bti => bti.TargetItem) .Where(bt => bt.BranchId == "9417") .AsEnumerable() .Select(bt => bt.BranchTargetItem.TargetItem) .OrderBy(bt => bt.TargetItemId) .ToList(); Debug.Assert(result.Count == 2); Debug.Assert(result[0].BranchTargetItem != null); Debug.Assert(result[0].BranchTargetItem.Count == 2); Debug.Assert(result[0].BranchTargetItem.First().BranchTargetItemId == 11); Debug.Assert(result[0].BranchTargetItem.First().BranchTarget != null); Debug.Assert(result[0].BranchTargetItem.First().BranchTarget.Count == 1); } } }
子句即可。 - 遍历返回的项目并执行其他查询。
- 只是渴望加载所有
.ThenInclude(ti => ti.TargetItems)
实体。
以下完全正常工作的示例项目演示了几种方法:
TargetItem
,
您可以使用略有不同的语法(linq-to-sql):
var query = (
from ti in _dbContext.TargetItems
join bti in _dbContext.BranchTargetItem on ti.Id = bti.ItemTargetItemId
join bt in _dbContext.BranchTarget on bti.Id = bt.BranchTargetItemId
where bt.BranchId.Equals("9417")
select new {
ti,btis = ti.BranchTargetItem,bts = ti.BranchTargetItem.SelectMany(x=> x.BranchTarget)
}
).ToListAsync();
这将在单次调用db时加载任何所需内容,而不会包含魔术。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。