You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 10:03:26