EF Core中多对多关系里如何访问父表属性?
问题:EF Core中通过中间表访问父表属性实现时间重叠校验
我有两张表(Ansatt、Stilling),通过中间表Ansatt_Stilling外键关联,该中间表实现一个Ansatt对应一个Stilling。Stilling表包含Start_Dato和Slutt_Dato字段,需求是一个Ansatt不能同时拥有多个Stilling(时间不重叠)。
在Ansatt_Stilling的Create控制器中,我希望实现类似如下的查询逻辑:
if (_context.Ansatt_Stillinger.Any(o => o.Stilling.Start_Dato== ansatt_Stilling.Stilling.Start_Dato))
想询问是否可以实现这类父表属性的访问?
相关实体模型与控制器代码
实体模型
Ansatt模型
public class Ansatt { [Key] public int Id { get; set; } [Required] public string Navn { get; set; } //Relationships public virtual ICollection<Ansatt_Stilling> Ansatt_Stillinger { get; set; } public virtual ICollection<Ansatt_Oppgave> Ansatt_Oppgaver { get; set; } }
Stilling模型
public class Stilling { [Key] public int Id { get; set; } [Required] public string Stilling_Navn { get; set; } [Required] public DateTime Start_Dato { get; set; } [Required] public DateTime Slutt_Dato { get; set; } // Relationships public virtual ICollection<Ansatt_Stilling> Ansatt_Stillinger { get; set; } }
Ansatt_Stilling模型
public class Ansatt_Stilling { public int ansatt_id { get; set; } public int stilling_id { get; set; } [ForeignKey("ansatt_id")] public virtual Ansatt Ansatt { get; set; } [ForeignKey("stilling_id")] public virtual Stilling Stilling { get; set; } }
控制器代码
Ansatt_Stilling控制器Create方法
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Create([Bind("ansatt_id,stilling_id")] Ansatt_Stilling ansatt_Stilling) { if (!ModelState.IsValid) { if (_context.Ansatt_Stillinger.Any(o => o.stilling_id == ansatt_Stilling.stilling_id) && _context.Ansatt_Stillinger.Any(o => o.ansatt_id == ansatt_Stilling.ansatt_id)) { //ViewBag.Duplikat = "Ansatt allerede har en stilling i den perioden"; ViewBag.Duplikat = "Allerede Registrert"; return View(); } else { bool isOverlapping = _context.Ansatt_Stillinger.Any(v => v.Stilling.Start_Dato == ansatt_Stilling.Stilling.Start_Dato); ViewBag.Duplikat = " Start dato"; _context.Add(ansatt_Stilling); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } } ViewData["ansatt_id"] = new SelectList(_context.Ansatter, "Id", "Navn", ansatt_Stilling.ansatt_id); ViewData["stilling_id"] = new SelectList(_context.Stillinger, "Id", "Stilling_Navn", ansatt_Stilling.stilling_id); return View(ansatt_Stilling); }
解决方案
可以实现,但需要注意以下几个关键问题:
1. 导航属性访问的前提
你的代码中ansatt_Stilling.Stilling大概率是null——因为Create方法通过Bind只接收了ansatt_id和stilling_id,并没有自动加载对应的Stilling实体。需要先从数据库中查询目标Stilling:
var targetStilling = await _context.Stillinger.FindAsync(ansatt_Stilling.stilling_id); if (targetStilling == null) { // 处理Stilling不存在的异常情况 ViewBag.Error = "指定的职位不存在"; return View(); }
同时,要访问Ansatt_Stilling关联的Stilling属性,需要确保EF Core能加载关联数据:
- 若启用了延迟加载(导航属性标记为
virtual,且配置了延迟加载代理),可以直接访问,但会产生N+1查询,性能较差。 - 更推荐用显式Include预先加载关联数据,避免额外查询:
var existingOverlapping = await _context.Ansatt_Stillinger .Include(asm => asm.Stilling) .AnyAsync(asm => asm.ansatt_id == ansatt_Stilling.ansatt_id && asm.Stilling.Start_Dato < targetStilling.Slutt_Dato && asm.Stilling.Slutt_Dato > targetStilling.Start_Dato);
2. 时间重叠逻辑修正
你当前只比较Start_Dato是否相等,无法覆盖所有重叠场景(比如新职位的时间段完全包含在已有职位时间段内)。正确的时间重叠判断公式是:
已有职位开始时间 < 新职位结束时间 且 已有职位结束时间 > 新职位开始时间
同时必须限定是同一个Ansatt,否则会错误校验所有员工的职位时间。
3. 中间表模型补充配置
你的Ansatt_Stilling模型未设置复合主键,EF Core无法正确识别这是关联表,容易引发数据异常。建议添加复合主键:
public class Ansatt_Stilling { [Key, Column(Order = 0)] public int ansatt_id { get; set; } [Key, Column(Order = 1)] public int stilling_id { get; set; } [ForeignKey("ansatt_id")] public virtual Ansatt Ansatt { get; set; } [ForeignKey("stilling_id")] public virtual Stilling Stilling { get; set; } }
或者通过DbContext的Fluent API配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Ansatt_Stilling>() .HasKey(asm => new { asm.ansatt_id, asm.stilling_id }); }
内容的提问来源于stack exchange,提问作者Elian Altalab
相关产品推荐
相关产品推荐

