【问题标题】:Query needs to be filtered after .ThenInclude using .Where clause查询需要在 .ThenInclude 之后使用 .Where 子句进行过滤
【发布时间】:2020-09-07 02:06:32
【问题描述】:

我正在使用以下查询来根据需要过滤我的 BranchId。

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);

以上表达式创建以下 SQL,我认为 .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 并包含它的关系实体(TargetItems > BranchTargetItems > BranchTargets)

//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=&gt; bti.BranchTarget).Where( bt=&gt; 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&lt;TargetItem&gt; TargetItems { get; set; } 当我应用下面的代码时,子节点为空。它只返回第一个节点。

    var targetItems = await _dbContext.BranchTargets
                    .Where(x => x.BranchId.Equals("9417"))
                    .Include(t => t.BranchTargetItem)
                    .ThenInclude(t => t.TargetItem)
                    .Select(x=> x.BranchTargetItem.TargetItem)
                    .ToListAsync(cancellationToken);

【问题讨论】:

  • .HasMany(x =&gt; x.YZ) 始终包含“many”为 0 的可能性。EF 并不是真正的智能查询构建器,因此即使 WHERE 子句包含满足以下条件的条件,它也会基于此 0 可能性构建连接有效地将结果限制在连接至少有一次命中的情况下。通常这不是问题,因为数据库端的 SQL 优化器会做 EF 没有做的优化。
  • 所以最后这只是对Filtered Include 的另一个请求。因此,对于“理想解决方案”,您必须等待 EF Core 5 - 请参阅 Gert Arnold 对链接帖子的回答和相应的 EF Core 5 feature announcement。此处无需添加任何内容 - 在此公告之前已被问了数百万次,但没有好的开箱即用解决方案。

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


【解决方案1】:

你应该担心的不是AnyAny -> 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);

【讨论】:

  • 感谢您的回答,我尝试了您的解决方案,但结果仍然相同。它从所有分支返回值。我已经更新了我的问题,你能再检查一下吗?
  • 通常情况下,您会将根实体上的谓词与Include 中的过滤混淆。您正在过滤TargetItems,但其中每一个都将包含其所有嵌套实体。 IE。还有x.BranchId != "9417".
【解决方案2】:

回答原问题

我正在使用以下查询来根据需要过滤我的 BranchId。

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 JOINs 并返回BranchTarget 实体,您可以从那里通过branchTargets[0].BranchTargetItem.TargetItem 访问您的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, BranchId = "9417", 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 =&gt; ti.TargetItems) 子句。
  • 遍历返回的项目并执行其他查询。
  • 只需急切加载所有 TargetItem 实体。

以下完全工作的示例项目演示了几种方法:

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class TargetItem
    {
        public int TargetItemId { get; set; }
        public int? ParentTargetItemId { get; set; }

        public TargetItem ParentTargetItem { get; set; }
        public ICollection<BranchTargetItem> BranchTargetItem { get; set; }
        public ICollection<TargetItem> TargetItems { 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_01")
                .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.HasMany(x => x.TargetItems)
                        .WithOne(x => x.ParentTargetItem)
                        .HasForeignKey(x => x.ParentTargetItemId);

                    entity.HasData(
                        new TargetItem {TargetItemId = 1, ParentTargetItemId = null},
                        new TargetItem {TargetItemId = 2, ParentTargetItemId = 1},
                        new TargetItem {TargetItemId = 3, ParentTargetItemId = 1},
                        new TargetItem {TargetItemId = 4, ParentTargetItemId = 2});
                });

            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, BranchId = "9417", BranchTargetItemId = 41},
                        new BranchTarget {BranchTargetId = 300, BranchId = "1234", BranchTargetItemId = 22});
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            SetupDatabase();

            Approach1();
            Approach2();
            Approach3();
        }

        private static void Approach1()
        {
            using var context = new Context();
            var targetItems = GetTargetItems(context);

            var handled = new HashSet<TargetItem>();
            var queue = new Queue<TargetItem>(targetItems);
            while (queue.TryDequeue(out var targetItem))
            {
                handled.Add(targetItem);
                context.Entry(targetItem)
                    .Collection(ti => ti.TargetItems)
                    .Load();

                foreach (var childTargetItem in targetItem.TargetItems)
                {
                    if (!handled.Contains(childTargetItem))
                    {
                        queue.Enqueue(childTargetItem);
                    }
                }
            }

            AssertTargetItems(targetItems);
        }

        private static void Approach2()
        {
            using var context = new Context();
            var targetItems = GetTargetItems(context);

            var handled = new HashSet<TargetItem>();
            var queue = new Queue<TargetItem>(targetItems);
            while (queue.TryDequeue(out var targetItem))
            {
                handled.Add(targetItem);

                var childTargetItems = context.TargetItems
                    .Where(ti => ti.ParentTargetItemId == targetItem.TargetItemId)
                    .ToList();

                foreach (var childTargetItem in childTargetItems)
                {
                    if (!handled.Contains(childTargetItem))
                    {
                        queue.Enqueue(childTargetItem);
                    }
                }
            }

            AssertTargetItems(targetItems);
        }

        private static void Approach3()
        {
            using var context = new Context();
            var targetItems = GetTargetItems(context);
            
            context.TargetItems.Load();
            
            AssertTargetItems(targetItems);
        }

        private static void AssertTargetItems(List<TargetItem> targetItems)
        {
            Debug.Assert(targetItems.Count == 2);
            Debug.Assert(targetItems[0].BranchTargetItem != null);
            Debug.Assert(targetItems[0].BranchTargetItem.Count == 2);
            Debug.Assert(targetItems[0].BranchTargetItem.First().BranchTargetItemId == 11);
            Debug.Assert(targetItems[0].BranchTargetItem.First().BranchTarget != null);
            Debug.Assert(targetItems[0].BranchTargetItem.First().BranchTarget.Count == 1);
            Debug.Assert(targetItems[0].TargetItemId == 1);
            Debug.Assert(targetItems[0].TargetItems != null);
            Debug.Assert(targetItems[0].TargetItems.Count == 2);
            Debug.Assert(targetItems[0].TargetItems.First().TargetItems != null);
            Debug.Assert(targetItems[0].TargetItems.First().TargetItems.Count == 1);
        }

        private static List<TargetItem> GetTargetItems(Context context)
            => 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();

        private static void SetupDatabase()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        }
    }
}

【讨论】:

  • 感谢您的详细回答,它按预期工作!
【解决方案3】:

如果您需要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, 
                (bti, ti) => new { TargetItems = ti, BranchTarget = bt })           
            .Where(x=> x.TargetItems.BranchId.Equals("9417"))
            .ToListAsync(cancellationToken);

查询本身需要测试并调整为正确的实体。

【讨论】:

    【解决方案4】:

    如果您只想要匹配 where 子句的 BranchTarget,则需要将其设为查询的主要详细记录,然后包含其父记录;

    var query = await _dbContext.BranchTarget
        .Where(x => x.BranchId.Equals("9417"))
        .Include(t => t.BranchTargetItems)
        .ThenInclude(t => t.TargetItems)
        .ToListAsync(cancellationToken);
    

    【讨论】:

    • 感谢您的回答,我和您提到的完全一样。但是我的 TargetItems 实体在这里与自身具有一对多的关系,[DataMember] public virtual ICollection&lt;TargetItem&gt; TargetItems { get; set; } 当我应用您推荐的方法时,我的子节点是空的。它只返回第一个节点。
    • 补充说明,我尝试返回.Select(x =&gt; x.BranchTargetItems.TargetItems)子节点仍然丢失。
    【解决方案5】:

    您可以使用稍微不同的语法(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 的单次调用中加载您想要的任何内容,而无需包含魔法。

    【讨论】:

      猜你喜欢
      • 2013-09-25
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 2017-02-25
      • 1970-01-01
      • 2022-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多