【问题标题】:How to set up multiple one-to-one relationships in Entity Framework Core?如何在 Entity Framework Core 中设置多个一对一关系?
【发布时间】:2020-02-12 19:10:23
【问题描述】:

我有两个实体说ParentChild;每个父级最多可以有两个子引用。我的实体设置如下:

class Parent
{
    [Key]
    public int ParentId { get; set; }

    public int PrimaryChildId{ get; set; }
    public Child PrimaryChild { get; set; }

    public int SecondaryChildId { get; set; }
    public Child? SecondaryChild { get; set; }
    // remaining properties
}

class Child 
{
    [Key]
    public int ChildId { get; set; }

    public int ParentId { get; set; }
    public Parent Parent {get; set; }

    // remaining child properties 
}

DbContext.OnModelCreating我有这个代码:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Parent>(builder =>
    {
        builder.HasOne(p => p.PrimaryChild);
        builder.HasOne(p => p.SecondaryChild);
    });
}

这还不足以实现我在这里想要实现的目标。我收到一个错误:

无法确定“父”类型的导航属性“Child.Parent”表示的关系。手动配置关系,或使用“[NotMapped]”属性或使用“OnModelCreating”中的“EntityTypeBuilder.Ignore”忽略此属性

我尝试从Child 实体建立关系,但我得到了不同的错误,因为这使我为同一个属性设置了两个关系。我不希望我的孩子有两个导航属性,因为我知道一次只会使用一个,因为这会导致模型混乱。

我在互联网上搜索了一下,但我没有找到以这种方式建立的关系。

【问题讨论】:

  • 我认为最好使用一对多。因为你有一个父母和许多孩子。一对一不是很好的理论
  • EF 如何知道哪个孩子是主要孩子,哪个孩子是次要孩子?我认为你在这里有一个多对一的关系。您可以在域逻辑中强制执行您自己的规则。您可以创建自己的 PrimaryChild 属性来获取集合中的第一个孩子或类似的东西。
  • @Anton,是的,这就是我开始想的。我总是可以在业务层强制执行限制,然后如果需求发生变化,这实际上会使将来变得更容易。不过,我很好奇这是否可能,所以我想我会让这个问题保持活跃。感谢您的意见。
  • 同意@chadnt。我在设计这个时只是考虑业务需求,如果我手动完成所有的数据库管理,这当然可以完成,但我认为为此放弃 ORM 的力量是不值得的。跨度>
  • @Jesse 好。我只是头脑风暴:)

标签: c# one-to-one ef-core-3.0


【解决方案1】:

过去几天我一直在尝试这样的事情,在尝试了各种数据注释和 Fluent API 废话之后,我能想出的最干净的解决方案结果证明是非常简单的,两者都不需要。它只需要向注入“DbContext”对象的父类添加一个“私有”构造函数(或者如果您使用延迟加载,则添加一个“受保护”构造函数)。只需将您的 'Parent' 和 'Child' 类设置为正常的一对多关系,并且现在可以从 'Parent' 实体中使用您的数据库上下文,您可以让 'PrimaryChild' 和 'SecondaryChild' 简单地返回一个使用 Find() 方法从数据库中查询。 Find() 方法也使用了缓存,所以如果你多次调用 getter,它只会访问一次数据库。

这是有关此功能的文档:https://docs.microsoft.com/en-us/ef/core/modeling/constructors#injecting-services

注意:“PrimaryChild”和“SecondaryChild”属性是只读的。要修改它们,请设置“PrimaryChildId”和“SecondaryChildId”属性。

class Parent
{
    public Parent() { }
    private MyDbContext Context { get; set; }
    // make the following constructor 'protected' if you're using Lazy Loading
    private Parent(MyDbContext Context) { this.Context = Context; }

    [Key]
    public int ParentId { get; set; }
    public int PrimaryChildId { get; set; }
    public Child PrimaryChild { get { return Context.Children.Find(PrimaryChildId); } }
    public int? SecondaryChildId { get; set; }
    public Child SecondaryChild { get { return Context.Children.Find(SecondaryChildId); } }
    // remaining properties
}

class Child
{
    [Key]
    public int ChildId { get; set; }
    public int ParentId { get; set; }
    public Parent Parent { get; set; }
    // remaining child properties 
}

【讨论】:

  • 我的要求已经改变,我什至没有这个设置了,但这是一个非常巧妙的解决方案。接受了答案,因为那肯定对我有用。
猜你喜欢
  • 1970-01-01
  • 2017-02-17
  • 1970-01-01
  • 2020-03-12
  • 2021-07-31
  • 2019-06-18
  • 2019-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多