You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EntityFramework中.Include(t => t.Nodes)无法返回关联Node实体问题

EF Core中.Include(t => t.Nodes)无法返回关联Node实体的问题

调用dataBaseContext.Tree.Include(t => t.Nodes).SingleOrDefault(t => t.Name == name)查询Tree时,已使用.Include(t => t.Nodes),但返回的Tree对象中Nodes集合为空,而数据库中存在对应Tree的Node数据。

问题代码片段

public class TreeRepository : ITreeRepository
{
    private DataBaseContext dataBaseContext;

    public TreeRepository(DataBaseContext dataBaseContext)
    {
        this.dataBaseContext = dataBaseContext;
    }

    public async Task<ITree> GetTreeAsync(string name)
    {
        var tree = dataBaseContext.Tree.Include(t => t.Nodes).SingleOrDefault(t => t.Name == name);

        if (tree == null)
        {
            var t = await dataBaseContext.Tree.AddAsync(new TreeTable { Name = name });
            await dataBaseContext.SaveChangesAsync();

            tree = t.Entity;
        }

        return tree;
    }
}

完整相关代码

DataBaseContext.cs

using Microsoft.EntityFrameworkCore;
using FxNet.Web.Def.Api.Diagnostic.DAL.Db.Tables;

namespace FxNet.Web.Def.Api.Diagnostic.DAL.Db
{
    public class DataBaseContext : DbContext
    {
        public DataBaseContext(DbContextOptions<DataBaseContext> options) : base(options) {}

        public DbSet<JournalTable> Journal { get; set; }
        public DbSet<NodeTable> Node { get; set; }
        public DbSet<TreeTable> Tree { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<TreeTable>().ToTable("Tree"); //.Navigation(e => e.Nodes).AutoInclude();
            modelBuilder.Entity<NodeTable>().ToTable("Node");
            modelBuilder.Entity<JournalTable>().ToTable("Journal");
        }
    }
}

NodeTable.cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using FxNet.Web.Def.Api.Diagnostic.DAL.Infrastructure.Entity;

namespace FxNet.Web.Def.Api.Diagnostic.DAL.Db.Tables
{
    [Table("Node")]
    public class NodeTable : INode
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }

        public long TreeId { get; set; }

        [ForeignKey("TreeId")]
        public virtual TreeTable Tree { get; set; }

        public long ParentNodeId { get; set; }

        public string Name { get; set; }
    }
}

TreeTable.cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using FxNet.Web.Def.Api.Diagnostic.DAL.Infrastructure.Entity;

namespace FxNet.Web.Def.Api.Diagnostic.DAL.Db.Tables
{
    [Table("Tree")]
    public class TreeTable : ITree
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }

        public string Name { get; set; }

        IEnumerable<INode> ITree.Nodes => Nodes;

        public virtual List<NodeTable> Nodes { get; set; }
    }
}

INode.cs

namespace FxNet.Web.Def.Api.Diagnostic.DAL.Infrastructure.Entity
{
    public interface INode
    {
        long Id { get; set; }

        long TreeId { get; set; }

        long ParentNodeId { get; set; }

        string Name { get; set; }
    }
}

ITree.cs

namespace FxNet.Web.Def.Api.Diagnostic.DAL.Infrastructure.Entity
{
    public interface ITree
    {
        long Id { get; set; }

        string Name { get; set; }

        IEnumerable<INode> Nodes { get; }
    }
}

Startup.cs

using System.Linq;
using System.Reflection;
using System.ComponentModel;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using FxNet.Web.Def.Api.Diagnostic.DAL.Db;
using FxNet.Web.Def.Api.Diagnostic.Middleware;
using Microsoft.Extensions.DependencyInjection;
using FxNet.Web.Def.Api.Diagnostic.DAL.Repository;
using FxNet.Web.Def.Api.Diagnostic.DAL.Infrastructure.Repository;

namespace FxNet.Web.Def.Api.Diagnostic
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddTransient<GlobalExceptionHandlerMiddleware>();
            services.AddSwaggerGen(c =>
            {
                c.CustomSchemaIds(x => x.GetCustomAttributes<DisplayNameAttribute>().SingleOrDefault()?.DisplayName ?? x.FullName);
            });

            services.AddTransient<IJournalRepository, JournalRepository>();
            services.AddTransient<INodeRepository, NodeRepository>();
            services.AddTransient<ITreeRepository, TreeRepository>();

            services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DataBaseConnectionString")));

            //services.AddDbContext<DataBaseContext>(options => options.UseNpgsql(Configuration.GetConnectionString("PostgreSQLConnectionString")));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "FxNet.Web.Def.Api.Diagnostic v1"));
            }

            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<DataBaseContext>();
                context.Database.EnsureCreated();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

解决方法

1. 显式配置EF Core实体关系

EF Core未自动识别TreeTable与NodeTable的一对多关联,需在OnModelCreating中显式配置:

修改DataBaseContext.cs中的OnModelCreating方法:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<TreeTable>().ToTable("Tree");
    modelBuilder.Entity<NodeTable>().ToTable("Node");
    modelBuilder.Entity<JournalTable>().ToTable("Journal");

    // 配置Tree与Node的一对多关系
    modelBuilder.Entity<TreeTable>()
        .HasMany(t => t.Nodes)
        .WithOne(n => n.Tree)
        .HasForeignKey(n => n.TreeId)
        .OnDelete(DeleteBehavior.Cascade); // 根据业务需求设置删除行为,可选
}

2. 统一异步操作

将Repository中的同步查询改为异步,避免混合同步异步操作引发的潜在问题:

修改TreeRepository.cs中的GetTreeAsync方法:

public async Task<ITree> GetTreeAsync(string name)
{
    var tree = await dataBaseContext.Tree.Include(t => t.Nodes).SingleOrDefaultAsync(t => t.Name == name);

    if (tree == null)
    {
        var t = await dataBaseContext.Tree.AddAsync(new TreeTable { Name = name });
        await dataBaseContext.SaveChangesAsync();

        tree = t.Entity;
    }

    return tree;
}

3. 验证数据正确性

确认数据库中Node记录的TreeId与目标Tree的Id完全匹配,排除数据不匹配导致的关联失效。


内容的提问来源于stack exchange,提问作者Pavel Zazulia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 13:49:50