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

时间:2018-11-05 15:23:31

标签: mysql ef-core-2.0 .net-core-2.0

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

重要的表格是商店库存电影表格。表之间是多对多关系,链接器表库存表。

我使用 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; }
}

库存

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; }
    }

电影

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; }

}

我的上下文如下所示:

  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");

最后,回购如下所示:

public IEnumerable<Store> GetStores()
{
    return _context.Store.
        Include(a => a.Inventory).
        ToList();
}

问题: 当我从Controller调用此方法以获取存储列表时,Postman上没有任何json响应。但是,如果我调试从Controller返回的列表,则会找到存储列表。 问题在于该列表包含: store-> inventory-> film-> store-> inventory-> film-> store ...等创建循环依赖项,以填充请求的允许进程内存。

可能的解决方案: 我认为这与以下事实有关:在 Context 上,两个外键均被定义为 HasIndex 而不是 HasKey

entity.HasIndex(e => new { e.StoreId, e.FilmId })
                 .HasName("idx_store_id_film_id");

当我将其定义为 HasKey 时,我会收到错误消息:

  

'从'Rental.Inventory'到'Inventory.Rental'的关系   外键属性{'InventoryId':int}无法定位主键   键{'StoreId':字节,“ FilmId”:短},因为它不兼容。   配置主键或一组兼容的外键   关系的属性。”

1 个答案:

答案 0 :(得分:1)

要回答@hamzas评论,我确实找到了解决此问题的方法。我使用EFCore通过脚手架(数据库优先)构建实体和DBContext。作为最佳实践,您应该使用模型(Dtos)代表客户端的数据。 EFCore对于帮助我们灵活地访问这种M对N关系非常有帮助。这使我们能够灵活地根据需要向客户表示此数据。
无论您使用哪种情况。您必须将M对N的关系转换为1对N的模型。
用例1:您要显示特定商店的所有电影。
解决方案
步骤1:您创建一个StoreDto(模型)

public class StoreDto
{
    int StoreId { get; set; }
    ICollection<FilmDto> Films { get; set; }
       = new List<FilmDto> ();
}

步骤2:创建FilmDto

public class FilmDto
{
    int FilmId { get; set; }
    int StoreId { get; set; }
    string FilmName { get; set; }
}

步骤3:您提供了带有自动映射器的映射

public class MappingProfiles : Profile 
{
    public MappingProfiles()
    {
        CreateMap<Store, StoreDto>();
        CreateMap<Film, FilmDto>();
    }
}

第4步::正确查询数据,不幸的是,我没有这个示例了,无法测试此代码,因此这里是您必须尝试的地方

public Store GetFilmsForStore(byte StoreId)
{
    
    return _context.Store.
        Include(a => a.Inventory).
        ThenInclude(i => i.Film)
        ToList();
}

在“包含”部分上,您只想获取其中StoreId == Inverntory.StoreId的Inventory条目,然后从结果列表中包含Films Object。 希望您能理解。您想破坏您的m到n关系,并使其与客户之间的关系从1到m。