Newtonsoft.Json.JsonSerializationException: Error getting value from 'MyEntity' on 'Castle.Proxies.SalonProxy'。

3

大家好,我有一个使用Entity Framework Core 6.0和Postgresql作为数据库的Asp.Net Core Web API项目。

我配置了实体及其关系。一切都很好,但是在通用仓库设计模式中出现了问题。

如果我只使用context.Products.ToList(),它可以完美地工作。但是每当我将其与通用存储库设计模式一起使用时,就会出现错误。但是有一件有趣的事情,只要我设置断点10秒甚至更长时间,并在10秒后保持它,就没有错误。一定是异步问题,我从未使用过吗?

错误: 输入图像描述

Newtonsoft.Json.JsonSerializationException:从'Seats'获取值时出错 'Castle.Proxies.SalonProxy'。

--- > System.InvalidOperationException:生成了一个错误警告 'Microsoft.EntityFrameworkCore.Infrastructure.LazyLoadOnDisposedContextWarning': 在相关的DbContext被处理后尝试懒加载导航'Seats.SalonProxy'。这个例外可以 通过将事件ID压制或记录 'CoreEventId.LazyLoadOnDisposedContextWarning'传递到方法'ConfigureWarnings'中,在'DbContext.OnConfiguring'或'AddDbContext'中。

public class EfEntityRepositoryBase<TEntity, TContext> : IEntityRepository<TEntity>
where TEntity : class, IEntity, new()
where TContext : DbContext, new()
{
    public TEntity Add(TEntity entity)
    {
        using (TContext context = new TContext())
        {
            var addedEntity = context.Entry(entity);
            addedEntity.State = EntityState.Added;
            context.SaveChanges();
            return addedEntity.Entity;
        }
    }

    

    public void Delete(TEntity entity)
    {
        using (TContext context = new TContext())
        {
            var deletedEntity = context.Entry(entity);
            deletedEntity.State = EntityState.Deleted;
            context.SaveChanges();
        }
    }

    public TEntity Get(Expression<Func<TEntity, bool>> filter)
    {
        using (TContext context = new TContext())
        {
            var result = context.Set<TEntity>().SingleOrDefault(filter);
            return result;
        }
    }

    public List<TEntity> GetAll(Expression<Func<TEntity, bool>> filter = null)
    {
        using (TContext context = new TContext())
        {
    
         var result = filter == null
          ? context.Set<TEntity>().ToList()
          : context.Set<TEntity>().Where(filter).ToList();
          return result;
        }
    }
}

}

我的沙龙实体

public class Salon : IEntity
{
    [Key]
    public int Id { get; set; }

    public string Name { get; set; }

    public string SalonNumber { get; set; }

    public virtual List<Seat> Seats { get; set; }

    public virtual List<Ticket> Tickets { get; set; }
}

我的座位实体

    public class Seat : IEntity
    {
        [Key]
        public int Id { get; set; }
    
        public int SeatNumber { get; set; }
    
        public int SalonId { get; set; }
        public virtual Salon Salon { get; set; }
    }

 

MyDbContext

public class AppDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseLazyLoadingProxies().ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.DetachedLazyLoadingWarning))
            .UseNpgsql("Server=localhost;Database=BiletsgoDB;Port=5432;Username=postgres;Password=123456");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Ticket>().HasOne(t => t.Category).WithMany(b => b.Tickets).HasForeignKey(t => t.CategoryId);
            modelBuilder.Entity<Ticket>().HasOne(t => t.Salon).WithMany(b => b.Tickets).HasForeignKey(t => t.SalonId);
            modelBuilder.Entity<Seat>().HasOne(t => t.Salon).WithMany(b => b.Seats).HasForeignKey(t => t.SalonId);
            modelBuilder.Entity<TicketFile>().HasOne(t => t.Ticket).WithMany(b => b.TicketFiles).HasForeignKey(t => t.TicketId);
        }

        public DbSet<Ticket> Tickets { get; set; }
        public DbSet<TicketFile> TicketFiles { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Seat> Seats { get; set; }
        public DbSet<Salon> Salons { get; set; }
    }

错误提示表明您在从GetGetAll方法返回后访问了Seats属性,但该属性在从方法返回(并处置您的db上下文)之前尚未实现。这是懒惰评估最好避免的许多原因之一,而应优先使用急切加载(通过.Include)。 - Kirk Woll
@KirkWoll非常感谢,我现在理解得更好了。你有什么建议吗?我如何在通用存储库设计模式中使用Include方法?因为我可能有很多实体。 - Gollum
我的观点是,与直接在代码中使用 DbContext 相比,通用存储库模式并没有提供太多价值。如果你使用该模式的原因是进行单元测试,可以考虑使用内存数据库。或者让你的存储库更加专注,仅返回始终完全填充的领域实体。(例如,为给定的 Salon 获取 Seats 的单独存储库方法,并且根本不包括在返回的 Salon 类型中) - Kirk Woll
1
非常感谢您的建议。我已经解决了,您说得对,这可能不是一种有效的方法。我将实施您的建议。同样地,我会将我的解决方案作为答案添加。@KirkWoll - Gollum
1个回答

0

我解决了这个问题

params 包含我们的实体关系

 public List<TEntity> GetAll(params Expression<Func<TEntity, object>>[] including)
    {
        using (TContext context = new TContext())
        {

            var include = context.Set<TEntity>().AsQueryable();
            including.ToList().ForEach(item =>
            {
                include = include.Include(item);
            });
            return include.ToList();
        }
    }

我从我的业务层运行这个方法

_salonDal.GetAll(t => t.Seats,t => t.Tickets)

但是我意识到这种方式并不高效。通用仓储库应该使用基本实体。这是我的观点。


这里不清楚你是在回答问题还是添加新信息。如果是后者,应该通过编辑问题来完成。 - Gert Arnold
@GertArnold 我解决了。我没有添加新的信息。这是一个解决方案。 - Gollum
请在48小时后选择您认为是可接受的答案,谢谢。 - AliReza Beigy
1
不要介绍List分配和闭包对象,只需使用旧的普通foreach - Svyatoslav Danyliv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接