【问题标题】:EF Core 2 Stopping Circular Dependency on Many to Many RelationshipEF Core 2 停止对多对多关系的循环依赖
【发布时间】:2020-07-11 16:14:49
【问题描述】:

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

重要的表是 storeinventoryfilm 表。表之间是多对多关系,链接器表inventory表。

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

最后,repo 如下所示:

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

问题: 当我从控制器调用此方法以获取商店列表时,我在 Postman 上没有收到任何 json 响应。然而,如果我调试从控制器返回的列表,我会找到商店列表。 问题是该列表包含: 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} 不能定位主 key {'StoreId' : byte, 'FilmId' : short} 因为它不兼容。 配置一个主键或一组兼容的外键 这种关系的属性。'

【问题讨论】:

  • 嘿,你找到解决办法了吗?

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


【解决方案1】:

为了回答@hamzas 的评论,我确实找到了解决这个问题的方法。我使用 EFCore 通过脚手架(DB First)构建实体和 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 步: 正确查询数据,很遗憾,我不再有这个示例来测试此代码,所以您必须在这里进行一些试验 em>

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

在“包含”部分,您只想获取 StoreId == Inverntory.StoreId 的库存条目,然后从结果列表中包含电影对象。 我希望你能明白它的要点。你想打破你的 m 对 n 关系,让他们看起来像你的客户的 1 对 m。

【讨论】:

    猜你喜欢
    • 2019-11-11
    • 2021-10-11
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 2017-10-26
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多