【问题标题】:Entity Framework Core One To Many with No Foreign Key on Master Table实体框架核心一对多,主表上没有外键
【发布时间】:2018-09-24 07:21:23
【问题描述】:

我们决定将所有枚举合并到一个 RefValue 表中,我至少在构思上没有问题,但我似乎无法让 Entity 一起玩。

我认为一旦您查看架构就应该有意义,但它的要点是,当涉及到枚举时,Person 对象将具有 2 个字段(例如,性别、关系状态、名称后缀等)。一个字段将存储值(例如“女性”),而另一个字段将存储该 RefValue 的 id(例如,某个 guid 将是存储/定义值的 RefValue 表的外键)。这样我们就不必在 RefValue 表上进行多次连接来查找这些属性值。

这是架构:

[Table("Person")]
public class Person : Base
{
    public Guid Id {get; set; }

    public string Name { get; set; }

    public string Gender { get; set; }
    public RefValue GenderRef { get; set; }

    public string RelationshipStatus { get; set; }
    public RefValue RelationshipStatusRef { get; set; }

    public string NameSuffix { get; set; }
    public RefValue NameSuffixRef { get; set; }
}

[Table("RefValue")]
public class RefValue : Base
{
    public Guid Id {get; set; }

    public string Type { get; set; }

    public string Value { get; set; }
}

在 Person 对象中,我真的希望 RefValue 属性只是作为 RefValue 表的外键的 Guid。我开始转向只为它们制作 RefValue 属性,因为如果我按照导航属性的概念来做,EFC 似乎会让事情变得更容易。但问题是,它非常坚持在 RefValue 表上有一列作为这些一对多关系的另一端。

我可以在 RefValue 表上添加额外的列,这样看起来更像这样:

[Table("RefValue")]
public class RefValue : Base
{
    public string Type { get; set; }

    public string Value { get; set; }

    public ICollection<Person> Genders { get; set; }

    public ICollection<Person> RelationshipStatus { get; set; }

    public ICollection<Person> NameSuffix { get; set; }
}

但无论我如何旋转上下文类的 OnModelCreating() 方法,我似乎都无法让它们一起玩。

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<RefValue>()
            .HasMany(p => p.Genders)
            .WithOne(p => p.GenderRef)
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<RefValue>()
            .HasMany(p => p.RelationshipStatus)
            .WithOne(p => p.RelationshipStatusRef)
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<RefValue>()
            .HasMany(p => p.NameSuffix)
            .WithOne(p => p.NameSuffixRef)
            .OnDelete(DeleteBehavior.SetNull);
    }

我也尝试过相反的方式,但我通常会收到Introducing FOREIGN KEY constraint ... on table ... may cause cycles or multiple cascade paths

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .HasOne(p => p.PersonTypeRef)
            .WithMany()
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<Person>()
            .HasOne(p => p.RelationshipStatusRef)
            .WithMany()
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<Person>()
            .HasOne(p => p.NameSuffixRef)
            .WithMany()
            .OnDelete(DeleteBehavior.SetNull);
    }

有没有什么方法可以创建这种一对多(或真正的多对一)关系,而不会出现级联问题或(尤其是)实体在非依赖表上创建影子属性?

【问题讨论】:

  • 我处理它们的方式是创建一个查找表,因为这些值不会经常更改。 LuGender(Code, Desc)。我将用FEM, FemaleMAL, Male 等填充它。然后我将亲自拥有一个名为Gender 的属性(与表名相同,除了Lu)。我发现Guids 的描述性不是很好。
  • 我不确定真正的问题是什么。集合导航属性不会向表中添加列。 FK 列位于另一侧(具有参考导航属性的列)。在您的情况下,您只是在PersonRefValue 之间有多个 多对一关系。这种关系设计的唯一问题是多个级联路径(无法告诉数据库 FK 使用不同的值),因此您不能使用级联选项,例如您应该配置与OnDelete(DeleteBehavior.Restrict) 的关系。
  • CodingYoshi,是的,我喜欢为查找代码使用一种缩写而不是 Guid 的想法,但是为了统一起见,所有模型都继承自一个基类,其中包括一个 Guid Id 和 4标准 createdDate/by/updatedDate/by,所以我只想在所有表中继续使用该系统。
  • Ivan Stoev,也许这是标准 EF 和 EF Core 之间的区别?这是一个link,他们讨论 EFC 如何创建阴影属性,特别是在一对多关系中。我认为你是对的,尽管这可能是级联的死胡同。我认为可能值得放弃级联删除来代替手动管理 FK 关系,因为我不喜欢 EF 强加其意见并随后创建紧密耦合的依赖关系(“紧密耦合”可能太强了)
  • EF Core 关系没有任何问题。事实上,多级联路径问题/限制源于 SqlServer 数据库。集合导航属性不是强制性的,但流畅的 API 必须准确反映它们的存在 (WithMany(p =&gt; p.Collection)) 或不存在 (WithMany())。那么你只需要使用.OnDelete(DeleteBehavior.Restrict)

标签: c# asp.net sql-server entity-framework-core


【解决方案1】:

让我们采用您原来的实体模型:

[Table("Person")]
public class Person : Base
{
    public Guid Id {get; set; }

    public string Name { get; set; }

    public string Gender { get; set; }
    public RefValue GenderRef { get; set; }

    public string RelationshipStatus { get; set; }
    public RefValue RelationshipStatusRef { get; set; }

    public string NameSuffix { get; set; }
    public RefValue NameSuffixRef { get; set; }
}

[Table("RefValue")]
public class RefValue : Base
{
    public Guid Id {get; set; }

    public string Type { get; set; }

    public string Value { get; set; }
}

使用 fluent API 配置单向(即仅在一侧具有导航属性)FK 关系绝对没有问题。您唯一需要考虑的是,由于该模型引入了多个级联路径,您不能使用删除级联选项,即您应该指定DeleteBehavior.Restrict

话虽如此,以下是上述模型的流畅配置:

modelBuilder.Entity<Person>()
    .HasOne(p => p.GenderRef)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Person>()
    .HasOne(p => p.RelationshipStatusRef)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Person>()
    .HasOne(p => p.NameSuffixRef)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

如果您添加集合导航属性,请不要忘记在相应的 WithMany 调用中指定它们,否则 EF Core 会创建额外的关系。

【讨论】:

  • 感谢 Ivan,是的,鉴于概念和 EF Core,这就是我完成它所需要的。仍在与我的团队一起讨论是否要处理此决定导致的行为,但这实际上是针对 POC 项目的。无论如何,我们最终可能会决定不使用它,但无论如何,这正是我所需要的。谢谢伊万。
【解决方案2】:

我们决定将所有枚举合并到一个 RefValue 表中

这让我很难过。你将如何阻止人们的性别为单身,或女性的关系状态?您将如何使用特定外键的所有有效值填充下拉列表?您将如何帮助报告作者正确加入表格?

我似乎无法让他们一起玩。

那是因为您正在尝试从根本上违反关系设计。这只是众多困难中的第一个,应该让您重新考虑这个设计。

【讨论】:

  • 他们似乎在RefValue 中有某种鉴别器(public string Type),所以我猜他们可以区分GenderRelationshipStatus 等“枚举”。
  • 是的,Ivan 关于Type 字段是正确的。确实,您可以在访问属性时忽略该字段,因此在 person 对象中使用不适当的值,但在任何其他字段中都不能这样做。获取人员对象后,如果您要访问编辑页面,您首先会执行 GET 以获取所有相关的RefValues 以获取任何潜在的下拉菜单。
猜你喜欢
  • 1970-01-01
  • 2019-02-23
  • 1970-01-01
  • 1970-01-01
  • 2021-09-03
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多