EF Core 2 停止对多对多关系的循环依赖

axe*_*ter 6 mysql .net-core-2.0 ef-core-2.0

我在 MySql 服务器上使用来自 MySql 的 Sakila 示例数据库。该图如下所示。 EERDiagram Sakila

重要的表是storeinventoryfilm表。该是多到许多表和关系连接表库存表。

我使用EFCore 2在一个新的 dotnetcore 项目中搭建了这个数据库。我正在尝试获取商店列表及其电影列表。

所述实体定义如下:

店铺

public class Store
{
    public Store()
    {
        Customer = new HashSet<Customer>();
        Inventory = new HashSet<Inventory>();
        Staff = new HashSet<Staff>();
    }

    public byte StoreId { get; set; }
    public byte ManagerStaffId { get; set; }
    public short AddressId { get; set; }
    public DateTimeOffset LastUpdate { get; set; }

    public Address Address { get; set; }
    public Staff ManagerStaff { get; set; }
    public ICollection<Customer> Customer { get; set; }
    public ICollection<Inventory> Inventory { get; set; }
    public ICollection<Staff> Staff { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

存货

public partial class Inventory
    {
        public Inventory()
        {
            Rental = new HashSet<Rental>();
        }

        public int InventoryId { get; set; }
        public short FilmId { get; set; }
        public byte StoreId { get; set; }
        public DateTimeOffset LastUpdate { get; set; }

        public Film Film { get; set; }
        public Store Store { get; set; }
        public ICollection<Rental> Rental { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

电影

public partial class Film
    {
 public Film()
 {
     FilmActor = new HashSet<FilmActor>();
     FilmCategory = new HashSet<FilmCategory>();
     Inventory = new HashSet<Inventory>();
 }

 public short FilmId { get; set; }
 public string Title { get; set; }
 public string Description { get; set; }
 public short? ReleaseYear { get; set; }
 public byte LanguageId { get; set; }
 public byte? OriginalLanguageId { get; set; }
 public byte RentalDuration { get; set; }
 public decimal RentalRate { get; set; }
 public short? Length { get; set; }
 public decimal ReplacementCost { get; set; }
 public string Rating { get; set; }
 public string SpecialFeatures { get; set; }
 public DateTimeOffset LastUpdate { get; set; }

 public Language Language { get; set; 
 public Language OriginalLanguage { get; set; }
 public ICollection<FilmActor> FilmActor { get; set; }
 public ICollection<FilmCategory> FilmCategory { get; set; }
 public ICollection<Inventory> Inventory { get; set; }
Run Code Online (Sandbox Code Playgroud)

}

我的上下文如下所示:

  modelBuilder.Entity<Inventory>(entity =>
  {
         entity.ToTable("inventory", "sakila");

         entity.HasIndex(e => e.FilmId)
             .HasName("idx_fk_film_id");

         entity.HasIndex(e => new { e.StoreId, e.FilmId })
             .HasName("idx_store_id_film_id");
Run Code Online (Sandbox Code Playgroud)

最后,repo如下所示:

public IEnumerable<Store> GetStores()
{
    return _context.Store.
        Include(a => a.Inventory).
        ToList();
}
Run Code Online (Sandbox Code Playgroud)

问题: 当我从控制器调用此方法以获取商店列表时,我在 Postman 上没有收到任何 json 响应。然而,如果我调试到从控制器返回的列表中,我会找到商店列表。问题是列表包含:store->inventory->film->store->inventory->film->store...等。创建一个循环依赖,填满请求允许的Process内存。

可能的解决方案: 我认为这与以下事实有关:在上下文中外键都被定义为HasIndex而不是HasKey

entity.HasIndex(e => new { e.StoreId, e.FilmId })
                 .HasName("idx_store_id_film_id");
Run Code Online (Sandbox Code Playgroud)

当我将它定义为HasKey 时,我收到一个错误:

'从'Rental.Inventory' 到'Inventory.Rental' 与外键属性 {'InventoryId' : int} 的关系不能定位主键 {'StoreId' : byte, 'FilmId' : short} 因为它不兼容。为这种关系配置一个主键或一组兼容的外键属性。

axe*_*ter 1

为了回答@hamzas 评论,我确实找到了这个问题的解决方案。我使用 EFCore 通过脚手架(DB First)构建实体和 DBContext。作为最佳实践,您应该使用模型 (Dtos) 来表示客户端的数据。EFCore 非常有帮助,让我们可以根据需要灵活地访问这种 M 到 N 关系。这使我们能够灵活地以我们想要的方式向客户端表示此数据。\n
无论您的用例是什么。您必须将 M 到 N 关系转换为 1 到 N 模型。\n
用例 #1:您想要显示特定商店的所有电影。
\n解决方案
\n步骤#1:创建 StoreDto(模型)

\n
public class StoreDto\n{\n    int StoreId { get; set; }\n    ICollection<FilmDto> Films { get; set; }\n       = new List<FilmDto> ();\n}\n
Run Code Online (Sandbox Code Playgroud)\n

第2步:创建 FilmDto

\n
public class FilmDto\n{\n    int FilmId { get; set; }\n    int StoreId { get; set; }\n    string FilmName { get; set; }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

步骤#3:使用自动映射器提供映射

\n
public class MappingProfiles : Profile \n{\n    public MappingProfiles()\n    {\n        CreateMap<Store, StoreDto>();\n        CreateMap<Film, FilmDto>();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

步骤#4:正确查询数据,不幸的是我不再有这个例子来测试这段代码,所以这里是你必须尝试一下的地方

\n
public Store GetFilmsForStore(byte StoreId)\n{\n    \n    return _context.Store.\n        Include(a => a.Inventory).\n        ThenInclude(i => i.Film)\n        ToList();\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在“包含”部分,您只想获取 StoreId == Inverntory.StoreId 的库存条目,然后从结果列表中包含电影对象。\n我希望您能了解其要点。您想要打破多对多的关系,让它们对您的客户来说看起来像是一对多的关系。

\n