【问题标题】:Find entities not in many-to-many relation查找不在多对多关系中的实体
【发布时间】:2021-10-19 08:46:55
【问题描述】:

我可能看错了,但我在 EF Core 3.1 中有一个基本的多对多代码优先设置,部门 DepartmentDay Day。

modelBuilder.Entity<DepartmentDay>(entity =>
{
    entity.HasKey(dd => new { dd.DepartmentId, dd.DayId });
    entity.HasOne(dp => dp.Day)
        .WithMany(p => p.DepartmentDays)
        .HasForeignKey(d => d.DayId);
    entity.HasOne(dp => dp.Department)
        .WithMany(p => p.DepartmentDays)
        .HasForeignKey(d => d.DepartmentId);
});

第一个问题:这种关系是否可以让我有几天不与部门联系?我需要这个,因为这与营业时间有关,并且希望有一个影响所有部门的通用日期,而不必与所有部门建立特定的联系。但正如我在开头所说的那样,我可能会以错误的方式看待这个。

第二个问题:如果第一个问题是真实有效的设置,我如何让那些日子没有连接到 Linq 查询中的部门?

到目前为止我所拥有的是(编辑:将 allDays 从 Hashset 更改为 List。)

var allDays = await _context.Days.ToListAsync();
var allDepartmentDays = _context.DepartmentDays.Select(dd => dd.DayId).ToHashSet();
var genericDays = allDays.Where(d => !allDepartmentDays.Contains(d.Id));

或者在此处使用原始查询以获得性能更好?

SELECT Id
FROM Day
WHERE Id NOT IN (SELECT DayId FROM DepartmentDay)

编辑 2:包括整个数据模型

public class Department
{
    public int Id { get; set; }
    public int DepartmentNr { get; set; }
    public string Service { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public string Address { get; set; }
    public string Postal { get; set; }
    public string City { get; set; }
    public string Url { get; set; }
    public string MapUrl { get; set; }
    public DateTime Created { get; set; }
    public string CreatedBy { get; set; }
    public DateTime? Updated { get; set; }
    public string UpdatedBy { get; set; }
    public ICollection<DepartmentPeriod> DepartmentPeriods { get; set; }
    public ICollection<DepartmentDay> DepartmentDays { get; set; }
}

public class DepartmentDay
{
    public int DepartmentId { get; set; }
    public int DayId { get; set; }
    public Department Department { get; set; }
    public Day.Models.Day Day { get; set; }
}

public class Day
{
    public int Id { get; set; }
    public int PeriodId { get; set; }
    public string Service { get; set; }
    public string City { get; set; }
    public DateTime? Date { get; set; }
    public DayOfWeek? DayOfWeek { get; set; }
    public DateTime? OpenTime { get; set; }
    public DateTime? CloseTime { get; set; }
    public bool IsClosed { get; set; }
    public string Description { get; set; }
    public DateTime Created { get; set; }
    public string CreatedBy { get; set; }
    public DateTime? Updated { get; set; }
    public string UpdatedBy { get; set; }

    public ICollection<DepartmentDay> DepartmentDays { get; set; }
    public virtual Period.Models.Period Period { get; set; }
}

modelBuilder.Entity<Day>(entity =>
{
    entity.HasOne(d => d.Period)
        .WithMany(p => p.Days)
        .HasForeignKey(d => d.PeriodId);          
});

初始问题中没有包含另一个关系,它可以回答我的第一个问题,即 Department 1-M DepartmentPeriod M-1 Period 1-1 Day。因此,Day 表中会有几天与 DepartmentDay 无关,而与 Period 无关,对吗?

【问题讨论】:

  • 对于复杂的查询,我通常只使用原始查询来提高性能
  • 这算不算复杂查询?
  • @Hyzac 您似乎缺少 Period 和 DepartmentPeriod 但由于它们不是您实际问题的一部分,我想它们不是必需的,可以被嘲笑

标签: c# sql entity-framework-core linq-to-sql ef-code-first-mapping


【解决方案1】:

第一个问题:它是可选的吗?没有您的数据类型确实是一个棘手的问题,但是假设您正确设置了导航属性,如果它们的键未命名为 {DataType}Id,您只需要指定导航属性,因此如果您这样命名它们,则不需要,否则您必须指定要使用哪些外键字段以及哪些是键。如果您不指定关系,实体框架将为您生成多对多关系的表格。

第二个问题: 就性能而言,如果您能够编写一个好的查询(您建议的甚至是您建议的查询,那么 SQL 查询将始终优于)

select a.id from [day] a left join departmentday b on a.id = b.dayid where b.dayid is null

即使这样也能表现得更好一些, 然而,就可测试性而言,sql 是一个问题,因为我们想要运行实体 dbcontext 的 InMemory 模型,然后您的 sql 至少通常无法执行。

所以问题是您是否真的需要额外的性能,并且是否可以编写一个 linq 查询,通过使其成为同一表达式的一部分而不是两个或多个表达式,从而有效地使 linq 引擎编写类似的查询。

在您的情况下,我可能会遗漏目前未提供的数据模型中的某些内容,但您似乎可以像这样得到您想要的:

_context.Days.Include(d => d.DepartmentDays).Where(!d.DepartmentDays.Any());

更新: 查看模型,似乎缺少一些模型工作,您正在寻找的投影可以按照建议的方式完成。

模型创建的更改/添加:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Department>()
        .HasKey(k => k.Id);

    modelBuilder.Entity<DepartmentDay>()
        .HasKey(k => new { k.DayId, k.Department });

    modelBuilder.Entity<Day>()            
        .HasOne(d => d.Period)
        .WithMany(p => p.Days)
        .HasPrincipalKey(k => k.PeriodId)
        .HasForeignKey(d => d.Id);
   
    //Notice your departmentid is not alone a foreign key in the relationship table, as departments can have one row per day
    modelBuilder.Entity<DepartmentDay>()
        .HasOne(a => a.Department)
        .WithMany(b => b.DepartmentDays)
        .HasPrincipalKey(p => p.Id)
        .HasForeignKey(f => new { f.DepartmentId, f.DayId });

    base.OnModelCreating(modelBuilder);
}

在上下文中,我们可以使用:

public List<Day> GetDaysWithoutAnyDepartmentDays()
{
    return Days
        .Include(i => i.DepartmentDays)
        .Where(x => !x.DepartmentDays.Any()
        ).ToList();
}

【讨论】:

  • 添加了数据模型
  • @Hyzac 请检查更新
猜你喜欢
  • 2018-06-07
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
  • 2017-01-06
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
  • 2013-04-25
相关资源
最近更新 更多