惰性加载器在不调试时无法正常工作

问题描述 投票:0回答:1

这就是我的程序中发生的情况:在一个地方打开上下文,用数据填充数据库,关闭上下文。 在程序的下一个位置,我再次打开上下文,将数据从数据库提取到缓存中(这是我的类),并用它执行一些操作。 如果您在调试模式下逐步查看程序的执行情况,惰性加载程序将按其应有的方式工作,所有相关数据均已加载并且工作正常进行。如果删除端点并运行程序,则会出现错误:

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}");
  }
c# entity-framework-core lazy-loading
1个回答
0
投票

您现在在

CashInit
中所做的实际上是显式加载。您查询跟踪的部落,然后查询子集合并明确分配它们。您可以使用
Include

更轻松地做到这一点

然后,保存结果并处理上下文。在上下文中,从数据库获取子集合的信息已经消失(或者可能由于加载实体的方式而从未明确设置)。当您稍后访问部落成员时,延迟加载代理会尝试从数据库中获取它,但会失败,因为数据库上下文已被释放。

在调试器中,您仍然会看到加载的实体,并且看起来很正常。

在此处给出的代码中,您可以简单地删除延迟加载代理,因为您显式加载了数据。如果您不需要在项目中的某个地方进行延迟加载,这就是我推荐的。 您还可以尝试以其他方式加载实体,可以使用

Include
或使用
context.Entry(..).Collection(..).Load()
进行显式加载,详细信息如下:https://learn.microsoft.com/en-us/ef/core/querying /相关数据/显式 有助于保留这些实体已经加载且不需要延迟加载的信息。 我已经有一段时间没有使用延迟加载了,并且对细节也不是 100% 确定。

© www.soinside.com 2019 - 2024. All rights reserved.