Entity Framework Core 2.1.2で遅延読み込みを有効にしていて、AsNoTrackingを使用してクエリを実行しています。 Includeを使用して、ナビゲーションプロパティ(コレクション)を取り込みます。
私のすべてのエンティティのコレクションに少なくとも1つの子がある場合、それはすべて正常に機能します。
ただし、エンティティに子がない場合、エラーが発生します。
System.InvalidOperationException: 'Microsoft.EntityFrameworkCore.Infrastructure.DetachedLazyLoadingWarning:警告のために生成されたエラー:ナビゲーションプロパティ' Children 'をタイプ' ParentProxy 'のデタッチされたエンティティに遅延ロードしようとしました。遅延読み込みは、分離されたエンティティまたは「AsNoTracking()」で読み込まれたエンティティではサポートされていません。
問題の再現を次に示します(NuGetを使用してMicrosoft.EntityFrameworkCore 2.1.2、Microsoft.EntityFrameworkCore.Proxies 2.1.2、Microsoft.EntityFrameworkCore.InMemory 2.1.2を取り込んだ後、コンソールアプリから実行できます)。
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace LazyLoadingIssue
{
public class Parent
{
public int Id { get; set; }
public string ParentName { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
public string ChildName { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
SetupDatabase(setupToFail: true);
PerformTest();
Console.WriteLine("Press any key to finish");
Console.ReadLine();
}
private static void PerformTest()
{
using (var db = new MyContext())
{
try
{
IQueryable<Parent> parents = db.Rounds.Include(r => r.Children).AsNoTracking();
foreach (Parent parent in parents)
{
Console.WriteLine($"Parent (Id={parent.Id}) '{parent.ParentName}'");
foreach (Child child in parent.Children)
{
Console.WriteLine($" - Child (Id={child.Id}, ParentId={child.ParentId}) '{child.ChildName}'");
}
}
Console.WriteLine("** WORKED **");
}
catch (Exception ex)
{
Console.WriteLine("** FAILED **");
Console.WriteLine(ex);
}
}
}
private static void SetupDatabase(bool setupToFail)
{
using (var db = new MyContext())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var parent1 = new Parent
{
ParentName = "First sample parent (has children)",
Children = new List<Child>
{
new Child {ChildName = "child-1"},
new Child {ChildName = "child-2"},
new Child {ChildName = "child-3"}
}
};
var parent2 = new Parent
{
ParentName = $"Second sample parent ({(setupToFail ? "with no children" : "has children")})",
Children = new List<Child>()
};
if (!setupToFail)
parent2.Children.Add(new Child {ChildName = "child-4"});
db.AddRange(parent1, parent2);
db.SaveChanges();
}
}
}
public class MyContext : DbContext
{
public DbSet<Parent> Rounds { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
// .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=_ModelApp;Trusted_Connection=True;Connect Timeout=5;ConnectRetryCount=0")
.UseInMemoryDatabase(databaseName: "_modelApp")
.UseLazyLoadingProxies()
;
}
}
}
私は何か間違ったことをしていますか?または、これはEF Coreのバグですか? (私は そこの問題 も投稿しました。)
後世のために、これが EF Coreチームからの応答 です。
これは、NoTrackingクエリではレイジーロードがサポートされていないためです(#10042)。レイジーロードが必要ないように見える場合は、スローしないようにしました。振り返ってみると、常に投げた方がいいかもしれません。警告は、DbContextOptionsBuilderのConfigureWarningsを使用してスローしないように構成できます。
誰かのために使用する場合に備えて、レイジーロードを使用せず、常に追跡されていないセットを返すように構成された2番目の "ReadOnlyRepository"を作成しました。このリポジトリは、エンティティへの変更を永続化しないクエリに使用します。結果セットは大きくなる可能性があり、適切に実行する必要がある場合に使用します。
public class ReadOnlyRepository : MainDbContextBase, IReadOnlyRepository
{
public ReadOnlyRepository(IConfigurationSettings configurationSettings)
: base(configurationSettings, false)
{
}
public IQueryable<T> Retrieve<T>() where T : class, IAmAnAggregateRoot
{
return GetDbSet<T>().AsNoTracking();
}
}
public class MainDbContextBase : DbContext
{
private readonly IConfigurationSettings configurationSettings;
private readonly bool useLazyLoading;
protected MainDbContextBase(IConfigurationSettings configurationSettings, bool useLazyLoading)
{
this.configurationSettings = configurationSettings;
this.useLazyLoading = useLazyLoading;
}
protected DbSet<T> GetDbSet<T>() where T : class
{
return Set<T>();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseLazyLoadingProxies(useLazyLoading)
.UseSqlServer(configurationSettings.ConnectionString);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
}
}
}
バグはありません。 .AsNoTrackingを使用して変更を追跡していないため、遅延読み込みは機能しません。クエリで.Include( "ChildEntity")を使用するか、.AsNoTrackingの使用をあきらめることができます。