首页 > 解决方案 > EF Core - 将实体映射到自定义类会引发循环引用的 stackoverflow

问题描述

我正在使用 asp.net core webapi 构建一个 API POC。我正在尝试将 EF Core 实体与 API 返回的对象分开,以便为不同的方法自定义它们。

为了实现这一点,我创建了将实体映射到的自定义类。当我尝试映射具有一对多或多对多关系的实体时(2x 一对多与 EF Core 中的中间实体),这会崩溃

EF 核心实体

public class Country
{
  [Key]
  public string Code { get; set; }
  public string Name { get; set; }

  // Relations
  public virtual ICollection<CountryRegion> CountryRegions { get; set; }
}
public class Region
{
  [Key]
  public string Code { get; set; }
  public string Name { get; set; }

  // Relations
  public virtual ICollection<CountryRegion> CountryRegions { get; set; }
}
public class CountryRegion
{
  public string CountryCode { get; set; }
  public virtual Country Country { get; set; }

  public string RegionCode { get; set; }
  public virtual Region Region { get; set; }
}

用于映射 Country 实体的 API 端自定义类示例

public class Country
{
  public string Code { get; set; }
  public string Name { get; set; }

  [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  public IList<CountryRegion> Regions { get; set; }

  public Country() { }

  public Country(Database.Models.Country country)
  {
    this.Code = country.Code;
    this.Name = country.Name;

    if (country.CountryRegions != null)
    {
      this.Regions = country.CountryRegions.Select(cr => new CountryRegion(cr.Region)).ToList();
    }
  }

  public static implicit operator Country(Database.Models.Country country)
  {
    return country != null ? new Country(country) : null;
  }
}

按照 Ivan Stoev 的建议,从 API 端包含 CountryRegion。像这样构建我的类可以解决问题,但是我必须在关系的每一侧创建一个额外的类(区域侧的 RegionCountry,它映射国家)。我试图做的是避免这种情况,例如“一个国家有很多用户。一个用户有一个国家”。我必须创建一个 Country 类,该类具有 Users 集合,而一个不能在 User 和 Country 端映射它的类,直觉上这两个类本身就足够了……如果这有意义吗?

public class CountryRegion
{
  public string Code { get; set; }
  public string Name { get; set; }

  public CountryRegion() { }

  public CountryRegion(Database.Models.Region region)
  {
    this.Code = region.Code;
    this.Name = region.Name;
  }

  public static implicit operator CountryRegion(Database.Models.Region region)
  {
    return region != null ? new CountryRegion(region) : null;
  }
}

EF 方面一切正常,但显然,在执行此映射时,EF 实体只会循环遍历关系并最终抛出StackOverflowException.

我几乎确信我正在以错误的方式处理这个问题。是否有一种模式已经解决了我忽略的这个问题?

标签: c#asp.net-web-apidesign-patternsentity-framework-core

解决方案


好的,所以我设法通过仅删除循环引用来解决需要卫星类来投射关系的每一端。这行得通,但我仍然觉得这有点尴尬,并从设计模式的角度提出了更多问题,也许有更优雅的方式来做到这一点?

public class Country
{
  public string Code { get; set; }
  public string Name { get; set; }

  [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  public IList<Region> Regions { get; set; }

  public Country() { }

  public Country(Database.Models.Country country)
  {
    this.Code = country.Code;
    this.Name = country.Name;

    if (country.CountryRegions != null)
    {
      this.Regions = country.CountryRegions
        .Select(cr => { cr.Region.CountryRegions = null; return new Region(cr.Region); })
        .ToList();
    }
  }

  public static explicit operator Country(Database.Models.Country country)
  {
    return country != null ? new Country(country) : null;
  }
}
public class Region
{
  public string Code { get; set; }
  public string Name { get; set; }

  [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  public IList<Country> Countries { get; set; }

  public Region() { }

  public Region(Database.Models.Region region)
  {
    this.Code = region.Code;
    this.Name = region.Name;

    if (region.CountryRegions != null)
    {
      this.Countries = region.CountryRegions
        .Select(cr => { cr.Country.CountryRegions = null; return new Country(cr.Country); })
        .ToList();
    }
  }

  public static explicit operator Region(Database.Models.Region region)
  {
    return region != null ? new Region(region) : null;
  }
}

推荐阅读