efcore2停止多对多关系的循环依赖

jv4diomz  于 2021-06-19  发布在  Mysql
关注(0)|答案(1)|浏览(540)

我正在mysql服务器上使用来自mysql的sakila示例数据库。图表如下所示。

重要的table是商店,库存和电影表。表之间的关系是多对多的,链接器表是inventory表。
我使用efcore2在一个新的dotnetcore项目中构建了这个数据库。我想弄一份商店的名单和他们的电影名单。
实体定义如下:
商店

  1. public class Store
  2. {
  3. public Store()
  4. {
  5. Customer = new HashSet<Customer>();
  6. Inventory = new HashSet<Inventory>();
  7. Staff = new HashSet<Staff>();
  8. }
  9. public byte StoreId { get; set; }
  10. public byte ManagerStaffId { get; set; }
  11. public short AddressId { get; set; }
  12. public DateTimeOffset LastUpdate { get; set; }
  13. public Address Address { get; set; }
  14. public Staff ManagerStaff { get; set; }
  15. public ICollection<Customer> Customer { get; set; }
  16. public ICollection<Inventory> Inventory { get; set; }
  17. public ICollection<Staff> Staff { get; set; }
  18. }

库存

  1. public partial class Inventory
  2. {
  3. public Inventory()
  4. {
  5. Rental = new HashSet<Rental>();
  6. }
  7. public int InventoryId { get; set; }
  8. public short FilmId { get; set; }
  9. public byte StoreId { get; set; }
  10. public DateTimeOffset LastUpdate { get; set; }
  11. public Film Film { get; set; }
  12. public Store Store { get; set; }
  13. public ICollection<Rental> Rental { get; set; }
  14. }

电影

  1. public partial class Film
  2. {
  3. public Film()
  4. {
  5. FilmActor = new HashSet<FilmActor>();
  6. FilmCategory = new HashSet<FilmCategory>();
  7. Inventory = new HashSet<Inventory>();
  8. }
  9. public short FilmId { get; set; }
  10. public string Title { get; set; }
  11. public string Description { get; set; }
  12. public short? ReleaseYear { get; set; }
  13. public byte LanguageId { get; set; }
  14. public byte? OriginalLanguageId { get; set; }
  15. public byte RentalDuration { get; set; }
  16. public decimal RentalRate { get; set; }
  17. public short? Length { get; set; }
  18. public decimal ReplacementCost { get; set; }
  19. public string Rating { get; set; }
  20. public string SpecialFeatures { get; set; }
  21. public DateTimeOffset LastUpdate { get; set; }
  22. public Language Language { get; set;
  23. public Language OriginalLanguage { get; set; }
  24. public ICollection<FilmActor> FilmActor { get; set; }
  25. public ICollection<FilmCategory> FilmCategory { get; set; }
  26. public ICollection<Inventory> Inventory { get; set; }

}
我的背景如下:

  1. modelBuilder.Entity<Inventory>(entity =>
  2. {
  3. entity.ToTable("inventory", "sakila");
  4. entity.HasIndex(e => e.FilmId)
  5. .HasName("idx_fk_film_id");
  6. entity.HasIndex(e => new { e.StoreId, e.FilmId })
  7. .HasName("idx_store_id_film_id");

最后,回购协议如下:

  1. public IEnumerable<Store> GetStores()
  2. {
  3. return _context.Store.
  4. Include(a => a.Inventory).
  5. ToList();
  6. }

问题:当我从控制器调用这个方法来获取我不知道的商店列表时´在postman上没有得到任何json响应。但是,如果我调试到从控制器返回的列表中,我会找到存储列表。问题是列表中包含:store->inventory->film->store->inventory->film->store。。。等等。创建一个循环依赖项来填充请求允许的进程内存。
可能的解决方案:我认为这与以下事实有关:在上下文中,两个外键都被定义为hasindex而不是haskey

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

当我将其定义为haskey时,会得到一个错误:
'外键属性为{inventoryid':int}的'rental.inventory'与'inventory.rental'之间的关系不能以主键{storeid':byte,'filmid':short}为目标,因为它不兼容。为此关系配置一个主键或一组兼容的外键属性。'

o3imoua4

o3imoua41#

为了回答@hamzas的评论,我确实找到了解决这个问题的方法。我使用efcore通过scaffolding(db-first)构建实体和dbcontext。作为最佳实践,您应该使用模型(dto)来表示客户机的数据。efcore非常有助于我们灵活地访问这种m-to-n关系。这使我们能够灵活地以我们想要的方式向客户机表示这些数据。
不管你的用例是什么。必须将m到n的关系转换为1到n的模型。
用例1:您希望显示特定商店的所有电影。
解决方案
第1步:创建storedto(模型)

  1. public class StoreDto
  2. {
  3. int StoreId { get; set; }
  4. ICollection<FilmDto> Films { get; set; }
  5. = new List<FilmDto> ();
  6. }

第二步:制作电影

  1. public class FilmDto
  2. {
  3. int FilmId { get; set; }
  4. int StoreId { get; set; }
  5. string FilmName { get; set; }
  6. }

第三步:使用自动Map器提供Map

  1. public class MappingProfiles : Profile
  2. {
  3. public MappingProfiles()
  4. {
  5. CreateMap<Store, StoreDto>();
  6. CreateMap<Film, FilmDto>();
  7. }
  8. }

第四步:正确地查询数据,不幸的是我没有´我们已经没有这个例子来测试这个代码了,所以这里是´我得试一下

  1. public Store GetFilmsForStore(byte StoreId)
  2. {
  3. return _context.Store.
  4. Include(a => a.Inventory).
  5. ThenInclude(i => i.Film)
  6. ToList();
  7. }

在“include”部分,您只想获取storeid==inverntory.storeid的库存条目,然后从结果列表中包括films对象。我希望你能理解。你想打破你的m对n关系,让他们看起来像你的客户1对m。

展开查看全部

相关问题