Потратил кучу времени, но не понимаю, почему .Include(t => t.Nodes)
не возвращает объекты 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;
}
}
Строка ниже
var tree = dataBaseContext.Tree.Include(t => t.Nodes).SingleOrDefault(t => t.Name == name)
инициализировать .Include(t => t.Nodes)
Узлы пусты
Узлы заполнены
.Include(t => t.Nodes)
должен возвращать объекты узлов, если они существуют
Прикрепите дополнительный код
Контекст базы данных.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");
}
}
}
Нодетабле.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; }
}
}
Древетабле.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; }
}
}
Стартап.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();
});
}
}
}
Как я обнаружил Learn.microsoft.com/en-us/ef/core/dbcontext-configuration, мы можем упростить конструкцию using инициализации dbContext в каждой функции (например, using (var context = new BloggingContext())). И это работает нормально, за исключением .Include()
Добавлены ключевые классы и интерфейсы, SQL проверит. Спасибо
SQL-запрос выглядит следующим образом: exec sp_executesql N'SELECT TOP(2) [t].[Id], [t].[Name] FROM [Tree] AS [t] WHERE [t].[Name] = @__name_0', N'@__name_0 nvarchar(4000)',@__name_0=N'Root1'. Никаких дополнительных подзапросов при вызове первой строки.
То же самое, Узлы пусты. Я также попытался переименовать свойство Nodes интерфейса.
Решено, я использовал System.Data.Entity в TreeRepository, изменился на Microsoft.EntityFrameworkCore, и все заработало. Магия
Решено! Я использовал старую библиотеку System.Data.Entity. Заменил его на Microsoft.EntityFrameworkCore, и метод Include начал работать правильно.
Вы должны отметить это как ответ.
Я не уверен, правильно ли я понимаю ваш вопрос.
.Include()
не меняет значение, возвращаемое запросом. Он автоматически загружает указанные свойства навигации с использованием одного и того же запроса, что потенциально позволяет сократить количество обращений к базе данных. https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager