.net 多对多关系实体框架时的无限循环

roejwanj  于 2023-05-19  发布在  .NET
关注(0)|答案(1)|浏览(143)

我正在使用.NET 7,我试图从partenaire表中获取所有数据,以及所有相关数据(多对多)。
我有这三个类:

namespace Kollofid.Core.Entities
{
    public class Partenaire : AuditableBaseEntity
    {
        public string Name { get; set; }
        public string? PhoneNumber { get; set; }
        public string? Fax { get; set; }
        public string? Email { get; set; }

        public Guid? CountryId { get; set; }
        public Country? Country { get; set; }
        public string? City { get; set; }
        public string? PostalCode { get; set; }
        public string? Address { get; set; }

        public string? TaxRegistration { get; set; }
        public string? NumeroRegistreCommerce { get; set; }
        public string? Note { get; set; }

        public IEnumerable<ActivityPartenaire>? PartenaireActivities { get; set; }
        public IEnumerable<PartenaireProductLine>? PartenaireProductLines { get; set; }
        public IEnumerable<PartenaireHSCode>? PartenaireHSCodes { get; set; }
        public virtual ICollection<PartenaireDetailsBancaire>? PartenaireDetailsBancaires { get; set; }

        [NotMapped]
        public IEnumerable<Guid>? Activities { get; set; }

        [NotMapped]
        public IEnumerable<Guid>? ProductLines { get; set; }

        [NotMapped]
        public string? CountryName { get; set; }
    }
}

namespace Kollofid.Core.Entities
{
    public class Activity : AuditableBaseEntity
    {
        public string Name { get; set; }
        public string? Description { get; set; }
        public bool Visible { get; set; }
        public IEnumerable<ActivityPartenaire> PartenaireActivities { get; set; }

        public Activity()
        {
            Visible = true;
        }
    }
}

namespace Kollofid.Core.Entities
{
    public class ActivityPartenaire
    {
        public Guid PartenaireId { get; set; }
        public Partenaire Partenaire { get; set; }
        public Guid ActivityId { get; set; }
        public Activity Activity { get; set; }
    }
}

在基础架构AppDbContext声明中

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.AppendGlobalQueryFilter<ISoftDelete>(s => s.DeletedOn == null);

    builder.Entity<ActivityPartenaire>().HasKey(pa => new { pa.ActivityId, pa.PartenaireId });
    builder.Entity<PartenaireProductLine>().HasKey(pa => new { pa.PartenaireId, pa.ProductLineId });
      
    base.OnModelCreating(builder);

    builder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}

ProductRepository中:

private IQueryable<Partenaire> GetAllAsQueryable()
{
    return _dbContext.Partenaires
                     .Include(x => x.PartenaireActivities)
                     .Include(x => x.PartenaireProductLines)
                     .Include(x => x.Country)
                     .Select(par => new Partenaire
                             {
                                 Id = par.Id,
                                 Name = par.Name,
                                 PhoneNumber = par.PhoneNumber,
                                 Fax = par.Fax,
                                 Email = par.Email,
                                 Address = par.Address,
                                 City = par.Address,
                                 PostalCode = par.Address,
                                 TaxRegistration = par.TaxRegistration,
                                 NumeroRegistreCommerce = par.NumeroRegistreCommerce,
                                 Note = par.Note,
                                 CountryId = par.CountryId,
                                 CountryName = par.Country.Name,
                                 PartenaireActivities = par.PartenaireActivities.ToList(),
                                 // ProductLines = par.PartenaireProductLines.Select(prod => prod.ProductLineId)
                             });
}

当我调用GetAllAsQueryable.ToList()时,我有一个无限循环,我现在得到了异常,屏幕上没有消息。
当我注解掉PartenaireActivitiesProductLines时,我得到了数据
我试着只用Guid填充Activities,但我遇到了同样的问题。
有人能告诉我是什么问题吗?
我还加了这个

services.AddControllers()
        .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
                options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
                options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
            });
ars1skjm

ars1skjm1#

好的,你把实体关系和投影混合在一起了,当涉及到投影时,不要把实体和投影混合在一起。
您还混合了一个用于返回的方法和IQueryable<TEntity>。这是我使用的一个常见的存储库模式,但应该是这样的:

private IQueryable<Partenaire> GetAllAsQueryable()
{
     return _dbContext.Partenaires;
}

在返回IQueryable时,不应尝试将数据投影到新的类示例中。EF将继续应用进一步的过滤,这样对它和一个Select(x = new TEntity)几乎肯定会扔在工程几扳手。如果一个像这样的方法和使用它的方法在同一个类中,那是非常没有意义的,只需要使用DbContext。
如果您的视图只需要数据的一个子集,那么定义一个简单的视图模型来表示该数据。不要重用实体对象来挑选数据。实体类的示例应该表示数据源中的完整实体。如果您需要视图的某些数据,请创建ViewModel。首先,您可以将查询简化为:

var results = _dbContext.Partenaires
    .Select(par => new PartenaireViewModel
    {
        Id = par.Id,
        Name = par.Name,
        PhoneNumber = par.PhoneNumber,
        Fax = par.Fax,
        Email = par.Email,
        Address = par.Address,
        City = par.City, // <- bug here, had par.Address
        PostalCode = par.PostalCode // and = par.Address here too.
        TaxRegistration = par.TaxRegistration,
        NumeroRegistreCommerce = par.NumeroRegistreCommerce,
        Note = par.Note,
        CountryId = par.CountryId,
        CountryName = par.Country.Name

        // PartenaireActivities = ??
        // ProductLines = ??
    });

在使用投影时,不需要使用Include提前加载。(Select
现在我们来谈谈活动和产品线。要为“活动”显示哪些数据?把这个放到投影里。例如,如果您希望ActivityId和Name可用,并且只希望Activity可见:

// ...
        PartenaireActivities = par.PartenaireActivities
            .Where(pa => pa.Visible)
            .Select(pa => new PartenairActivitiesViewModel
            {
                ActivityId = pa.ActivityId,
                Name = pa.Activity.Name
            }).ToList();

然后是类似的产品线。定义一个简单的视图模型来满足视图的需要,并将其投影下来。
尝试将实体引用传递给视图引擎时,您会遇到各种问题。当您将Partenaire实体向下投影到类的副本中时,您尝试填充Activities和ProductLines的代码只是设置对DbContext中真实的实体的引用,这将包括对真实Partenaire的引用等等。投影到ViewModel可以避免这些问题,并产生更好的查询,副作用更少,但您必须致力于向下投影整个过程,并且不设置实体引用,即使您认为“如果它具有与实体完全相同的字段,那又有什么意义呢?””

相关问题