EF Core 循环关系映射
EF Core Circular Relationship Mapping
给定以下实体模型:
public class Workshop
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<QuoteRequest> QuoteRequests { get; set; }
}
public class QuoteRequest
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Guid WorkshopId { get; set; }
public bool Responded { get; set; }
public decimal? Amount { get; set; }
public virtual Customer Customer { get; set; }
public virtual Workshop Workshop { get; set; }
}
以及以下 2 个视图模型:
public class WorkshopModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<QuoteRequestModel> QuoteRequests { get; set; }
}
public class QuoteRequestModel
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Guid WorkshopId { get; set; }
public bool Responded { get; set; }
public decimal? Amount { get; set; }
public CustomerModel Customer { get; set; }
public WorkshopModel Workshop { get; set; }
}
接下来,给出以下查询:
public async Task<Workshop> GetWorkshopAsync(Guid id, bool includeQuotes = false)
{
IQueryable<Workshop> query = _context.Workshops;
if (includeQuotes)
{
query = query.Include(w => w.QuoteRequests);
}
return await query.FirstOrDefaultAsync(w => w.Id == id);
}
无论我做什么,我都无法让 EF 在查询 Workshop
时不给我循环关系。比如我查询一个Workshop
有14个QuoteRequests
,每个有一个Workshop
,每个有14个QuoteRequests
等等等等:
我确实将 json 序列化程序引用循环处理设置设置为忽略,但这并没有给我想要的结果
services.AddControllers()
.AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
我想在我的映射配置文件中删除这个圆圈。我正在使用自动映射器。我已经设法从 QuoteRequest
端用我的映射配置文件打破循环引用:
CreateMap<QuoteRequestModel, QuoteRequest>()
.ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
.ForMember(dest => dest.Workshop, opt => opt.MapFrom(src => src.Workshop))
.ForPath(dest => dest.Customer.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequest>()))
.ForPath(dest => dest.Workshop.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequest>()));
CreateMap<QuoteRequest, QuoteRequestModel>()
.ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
.ForMember(dest => dest.Workshop, opt => opt.MapFrom(src => src.Workshop))
.ForPath(dest => dest.Customer.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequestModel>()))
.ForPath(dest => dest.Workshop.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequestModel>()));
这可能是一个有点卡顿的解决方案,但现在当我查询个人 QuoteRequest
时它可以正常工作。我想弄清楚的是如何在 Workshop
端的映射配置文件中执行相同的操作:
CreateMap<WorkshopModel, Workshop>()
.ForMember(dest => dest.QuoteRequests, opt => opt.MapFrom(src => src.QuoteRequests));
CreateMap<Workshop, WorkshopModel>()
.ForMember(dest => dest.QuoteRequests, opt => opt.MapFrom(src => src.QuoteRequests));
我无法真正针对 QuoteRequests
的每次迭代设置 Workshop
默认值。
这不是问题。您看到的是 EF 的对象修复。因为它的对象缓存中已经有这些实体,所以它会自动 "fixes up" 每个实体的关系,而无需再次查询任何内容。
唯一一次这可能是序列化期间的问题,因为序列化将尝试无限期地递归向下钻取。然而,根据序列化方法的不同,有不同的方法来防止这种递归序列化。此外,无论如何,您真的不应该直接序列化实体。相反,您应该将它们映射到 DTO 类,然后您可以在其中定义一个不会遇到相同递归问题的更基本的结构。然后,您将序列化 DTO,而不是实体。
给定以下实体模型:
public class Workshop
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<QuoteRequest> QuoteRequests { get; set; }
}
public class QuoteRequest
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Guid WorkshopId { get; set; }
public bool Responded { get; set; }
public decimal? Amount { get; set; }
public virtual Customer Customer { get; set; }
public virtual Workshop Workshop { get; set; }
}
以及以下 2 个视图模型:
public class WorkshopModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<QuoteRequestModel> QuoteRequests { get; set; }
}
public class QuoteRequestModel
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Guid WorkshopId { get; set; }
public bool Responded { get; set; }
public decimal? Amount { get; set; }
public CustomerModel Customer { get; set; }
public WorkshopModel Workshop { get; set; }
}
接下来,给出以下查询:
public async Task<Workshop> GetWorkshopAsync(Guid id, bool includeQuotes = false)
{
IQueryable<Workshop> query = _context.Workshops;
if (includeQuotes)
{
query = query.Include(w => w.QuoteRequests);
}
return await query.FirstOrDefaultAsync(w => w.Id == id);
}
无论我做什么,我都无法让 EF 在查询 Workshop
时不给我循环关系。比如我查询一个Workshop
有14个QuoteRequests
,每个有一个Workshop
,每个有14个QuoteRequests
等等等等:
我确实将 json 序列化程序引用循环处理设置设置为忽略,但这并没有给我想要的结果
services.AddControllers()
.AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
我想在我的映射配置文件中删除这个圆圈。我正在使用自动映射器。我已经设法从 QuoteRequest
端用我的映射配置文件打破循环引用:
CreateMap<QuoteRequestModel, QuoteRequest>()
.ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
.ForMember(dest => dest.Workshop, opt => opt.MapFrom(src => src.Workshop))
.ForPath(dest => dest.Customer.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequest>()))
.ForPath(dest => dest.Workshop.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequest>()));
CreateMap<QuoteRequest, QuoteRequestModel>()
.ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
.ForMember(dest => dest.Workshop, opt => opt.MapFrom(src => src.Workshop))
.ForPath(dest => dest.Customer.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequestModel>()))
.ForPath(dest => dest.Workshop.QuoteRequests, opt => opt.MapFrom(src => new List<QuoteRequestModel>()));
这可能是一个有点卡顿的解决方案,但现在当我查询个人 QuoteRequest
时它可以正常工作。我想弄清楚的是如何在 Workshop
端的映射配置文件中执行相同的操作:
CreateMap<WorkshopModel, Workshop>()
.ForMember(dest => dest.QuoteRequests, opt => opt.MapFrom(src => src.QuoteRequests));
CreateMap<Workshop, WorkshopModel>()
.ForMember(dest => dest.QuoteRequests, opt => opt.MapFrom(src => src.QuoteRequests));
我无法真正针对 QuoteRequests
的每次迭代设置 Workshop
默认值。
这不是问题。您看到的是 EF 的对象修复。因为它的对象缓存中已经有这些实体,所以它会自动 "fixes up" 每个实体的关系,而无需再次查询任何内容。
唯一一次这可能是序列化期间的问题,因为序列化将尝试无限期地递归向下钻取。然而,根据序列化方法的不同,有不同的方法来防止这种递归序列化。此外,无论如何,您真的不应该直接序列化实体。相反,您应该将它们映射到 DTO 类,然后您可以在其中定义一个不会遇到相同递归问题的更基本的结构。然后,您将序列化 DTO,而不是实体。