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
相关产品推荐
相关产品推荐

