这就是我的程序中发生的情况:在一个地方打开上下文,用数据填充数据库,关闭上下文。 在程序的下一个位置,我再次打开上下文,将数据从数据库提取到缓存中(这是我的类),并用它执行一些操作。 如果您在调试模式下逐步查看程序的执行情况,惰性加载程序将按其应有的方式工作,所有相关数据均已加载并且工作正常进行。如果删除端点并运行程序,则会出现错误:
System.InvalidOperationException:“生成了警告错误 'Microsoft.EntityFrameworkCore.Infrastruct.LazyLoadOnDisposeContextWarning': 尝试延迟加载导航 关联的 DbContext 后的“TrackedClanProxy.ClanMembers” 处置。可以通过传递事件来抑制或记录此异常 ID“CoreEventId.LazyLoadOnDisposeContextWarning”到 “DbContext.OnConfiguring”中的“ConfigureWarnings”方法或 ‘添加DbContext’。”
我使用实体框架核心 7.0.5 并使用相同版本的 efc 代理。 我的项目在依赖关系和数据库结构方面相当复杂,因此我不会附加大段代码,但以下是我的代码似乎符合使用 EFC 规则的证据:
这是我的上下文中启用惰性加载程序的地方:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlite(ConnectionString);
}
这是我的班级,我首先正确填写并放入数据库,然后尝试提取与 TrackedClan 相关的所有数据。正如你所看到的 - 字段是公共的、虚拟的。
public class TrackedClan
{
public int Id { get; set; }
public DateTime UpdatedOn { get; set; }
public bool IsCurrent { get; set; }
public string Tag { get; set; }
public string Name { get; set; }
public virtual ICollection<ClanMember> ClanMembers { get; set; }
public virtual ICollection<ClanWar> ClanWars { get; set; }
public virtual ICollection<CapitalRaid> CapitalRaids { get; set; }
public TrackedClan()
{
ClanMembers = new HashSet<ClanMember>();
ClanWars = new HashSet<ClanWar>();
CapitalRaids = new HashSet<CapitalRaid>();
}
}
最初,我试图在没有这个 foreach 循环(代码缺点)的情况下进行部落战争资本袭击和部落成员的笨拙分配,但我认为这可能会有所帮助(尽管这些相关实体本身内部有自己的子实体,我不认为这是正确的方法 - 明确地从上到下分配所有内容)。
我尝试将上下文设置为
using var db = new AppDbContext("path");,
但也喜欢
using (AppDbContextdb = new AppDbContext("path", true)){ }
但这也没有帮助) 当然,我尝试用谷歌搜索所有与我的问题至少有些相似的内容。
我不太明白这是怎么可能的——当你通过调试监视变量的内容时,一切都很好,只要你一转身,程序就会中断。某种薛定谔的惰性加载器 -__-
//Here i put data from DB to static class field
public class Cash
{
public static List<TrackedClan> TrackedClans { get; set; } = new List<TrackedClan>();
static Cash()
{
Console.WriteLine("Cash initializing started...");
CashInit();
Console.WriteLine("Cash initializing successful");
}
private static void CashInit()
{
using var db = new AppDbContext("Data Source=./../../../../CustomSolutionElements/CoCStatsTracker.db");
TrackedClans = db.TrackedClans.ToList();
foreach (var clan in TrackedClans)
{
clan.ClanWars = db.ClanWars.Where(x => x.TrackedClanId == clan.Id).ToList();
clan.CapitalRaids = db.CapitalRaids.Where(x => x.TrackedClanId == clan.Id).ToList();
clan.ClanMembers = db.ClanMembers.Where(x => x.TrackedClanId == clan.Id).ToList();
}
db.Complete();
}
}
//Another place where i try to obtain it:
foreach (var member in Cash.TrackedClans.First(x => x.Tag == "#YPPGCCY8").ClanMembers)
{
Console.WriteLine(@$"{member.Name}");
}
您现在在
CashInit
中所做的实际上是显式加载。您查询跟踪的部落,然后查询子集合并明确分配它们。您可以使用 Include
更轻松地做到这一点
然后,保存结果并处理上下文。在上下文中,从数据库获取子集合的信息已经消失(或者可能由于加载实体的方式而从未明确设置)。当您稍后访问部落成员时,延迟加载代理会尝试从数据库中获取它,但会失败,因为数据库上下文已被释放。
在调试器中,您仍然会看到加载的实体,并且看起来很正常。
在此处给出的代码中,您可以简单地删除延迟加载代理,因为您显式加载了数据。如果您不需要在项目中的某个地方进行延迟加载,这就是我推荐的。 您还可以尝试以其他方式加载实体,可以使用
Include
或使用 context.Entry(..).Collection(..).Load()
进行显式加载,详细信息如下:https://learn.microsoft.com/en-us/ef/core/querying /相关数据/显式
有助于保留这些实体已经加载且不需要延迟加载的信息。
我已经有一段时间没有使用延迟加载了,并且对细节也不是 100% 确定。