【问题标题】:Entity Framework with optional child navigation property creates extra columns具有可选子导航属性的实体框架创建额外的列
【发布时间】:2014-02-12 00:45:41
【问题描述】:

我刚刚注意到 Entity Framework 如何创建 Code-First DB 的一个相当奇怪的行为,当您有一个模型,其父模型具有子列表,但也有一个特定子的可选导航属性:我结束在孩子上增加了一个我没想到的额外的可为空的外键列。谁能解释一下这个专栏是否真的有必要?就此而言,任何人都可以提出一种更好的方法来表明特定的孩子被选中/活跃。

详细说明:

鉴于此模型:

public class Parent
{
    public int Id { get; set; }

    public virtual List<Child> Children { get; set; }

    // Optional navigation property to one of the child objects.
    public int? ActiveChildId { get; set; }
    public virtual Child ActiveChild { get; set; }
}

public class Child
{
    public int Id { get; set; }

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

我最终得到以下数据库:

CREATE TABLE [dbo].[Parents](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ActiveChildId] [int] NULL,
 CONSTRAINT [PK_dbo.Parents] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Parents]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Parents_dbo.Children_ActiveChildId] FOREIGN KEY([ActiveChildId])
REFERENCES [dbo].[Children] ([Id])
GO

ALTER TABLE [dbo].[Parents] CHECK CONSTRAINT [FK_dbo.Parents_dbo.Children_ActiveChildId]
GO

CREATE TABLE [dbo].[Children](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ParentId] [int] NOT NULL,
    [Parent_Id] [int] NULL,
 CONSTRAINT [PK_dbo.Children] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Children]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Children_dbo.Parents_Parent_Id] FOREIGN KEY([Parent_Id])
REFERENCES [dbo].[Parents] ([Id])
GO

ALTER TABLE [dbo].[Children] CHECK CONSTRAINT [FK_dbo.Children_dbo.Parents_Parent_Id]
GO

ALTER TABLE [dbo].[Children]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Children_dbo.Parents_ParentId] FOREIGN KEY([ParentId])
REFERENCES [dbo].[Parents] ([Id])
GO

ALTER TABLE [dbo].[Children] CHECK CONSTRAINT [FK_dbo.Children_dbo.Parents_ParentId]
GO

即子节点上同时有 ParentId (NOT NULL) 列和子节点上的 Parent_Id (NULL) 列。

在我看来,既然我们已经有了一个 1:N 外键关系父子关系,那么通过添加一个单向 1:[0 或 1] 父子关系它不应该创建另一个外键孩子的关键列。

【问题讨论】:

    标签: entity-framework


    【解决方案1】:

    添加InverseProperty属性:

    public class Child
    {
        public int Id { get; set; }
    
        public int ParentId { get; set; }
    
        [InverseProperty( "Children" )]
        public virtual Parent Parent { get; set; }
    }
    

    或者通过 Fluent API 映射关系并将 FK 指定为ParentId

            modelBuilder.Entity<Parent>()
                .HasMany( p => p.Children )
                .WithRequired( c => c.Parent )
                .HasForeignKey( c => c.ParentId );
    

    【讨论】:

    • 不幸的是,我认为这些建议中的任何一个都没有帮助。如果我将 ParentId 设为可为空,则它不再适合我的 1:N 关系,并且 Entity Framework 已经根据命名约定将 ParentId 推断为 Parent 的外键。
    • 如果您不想要可以为空的ParentId,请将RequiredAttribute 添加到属性中
    • 添加了流畅的api配置
    • 啊——太棒了!您的示例确实有效,但使它起作用的不是 RequiredAttribute - 它是 InversePropertyAttribute。如果没有这个,EntityFramework 似乎无法确定这是哪个关系的导航属性(这对我来说似乎是合理的)。为了不混淆任何阅读此内容的人,我是否可以建议您更新您的答案以删除RequiredAttribute,然后我将其标记为解决方案。再次感谢 Moho。
    • 答案肯定让我们能够读取数据,但写入它会导致“无法确定依赖操作的有效顺序。依赖关系可能由于外键约束、模型要求或存储而存在- 生成的值。”使用 (var db = new Context()) { var parent = new Parent(); var child = new Child(); parent.ActiveChild = 孩子; db.Parents.Add(父); db.SaveChanges(); }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多