【问题标题】:How to apply correctly a relationship of 1 to 1 in EF6如何在 EF6 中正确应用 1 对 1 的关系
【发布时间】:2021-07-29 15:30:41
【问题描述】:

所以我想将 1 到 1 的关系从一个表应用到另一个表,每个表都有导航属性,并且至少有一个模型可以访问外键。

假设这个例子

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }

    public int ContactId { get; set; }
    public virtual Contact Contact { get; set; }
}

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual User User { get; set; }
}

modelBuilder.Entity<User>().HasOptional<Contact>(u=> u.Contact)
    .WithRequired(c => c.User).Map(m => m.MapKey("ContactId")).

类似于此堆栈溢出问题中使用的相同示例:

EF Code First - 1-to-1 Optional Relationship

问题在于它给出了一个错误,指出属性名称“ContactId”已定义。

但我想在数据库和模型上都定义这个外部属性,以便我可以使用例如 linq:

this.dbContextProvider.CurrentContext.User.SingleOrDefault(src => src.ContactId == contactId);

或者这是可以接受的还是非常无能的:

this.dbContextProvider.CurrentContext.User.SingleOrDefault(src => src.Contact.Id == contactId);

这最后一个选项将在查询数据库时在两个表之间创建一个连接,对吧?

【问题讨论】:

  • 不要对 ef 6 使用 modelbuilder。使用注释更容易:[ForeignKey("Contact")] above public int ContactId {get;set;}
  • @GHDevOps 可以使用数据注释完成的所有操作也可以使用 Fluent API / ModelBuilder 来实现。反之则不然。

标签: c# .net entity-framework entity-framework-6 ef-fluent-api


【解决方案1】:

正确模型(即没有明确的User.ContactId 属性)的缺点是实际上它仍然是 1:n 关系。数据库不强制执行 1:1。这只是一个FK。在 EF6 中建立真正的数据库强制 1:1 关联的唯一方法是依赖实体(此处为:用户)具有一个主键,该主键也是主体实体(联系人)的外键:

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }

    //public int ContactId { get; set; } <= removed
    public virtual Contact Contact { get; set; }
}

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual User User { get; set; }
}

还有:

modelBuilder.Entity<User>()
           .HasRequired<Contact>(u => u.Contact)
           .WithOptional(c => c.User);

这会生成以下数据库架构:

CREATE TABLE [dbo].[Users] (
    [Id] [int] NOT NULL,
    [Username] [nvarchar](max),
    CONSTRAINT [PK_dbo.Users] PRIMARY KEY ([Id])
)

CREATE TABLE [dbo].[Contacts] (
    [ID] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    CONSTRAINT [PK_dbo.Contacts] PRIMARY KEY ([ID])
)

CREATE INDEX [IX_Id] ON [dbo].[Users]([Id])

ALTER TABLE [dbo].[Users] ADD CONSTRAINT [FK_dbo.Users_dbo.Contacts_Id]
    FOREIGN KEY ([Id]) REFERENCES [dbo].[Contacts] ([ID])

至于查询,在像 context.Users.Where(u =&gt; u.Contact.ID == 4) 这样的查询中,EF6 会注意到没有请求 Contact 字段,它会将 FK 短路到 User.Id,即没有连接。但是当然,在这个设置中,你也可以使用context.Users.Where(u =&gt; u.Id == 4)

EF 核心中,可以通过以下映射使用您的模型,User.ContactId

modelBuilder.Entity<User>()
    .HasOne(u => u.Contact)
    .WithOne(c => c.User)
    .HasForeignKey<User>(u => u.ContactId);

EF 核心足够智能,可以在 User.ContactId 上创建一个唯一索引,因此这是一个数据库强制 1:1 关联与单独的 FK。

【讨论】:

  • 这相当于:如果您需要一个单独的 FK 字段但被 EF6 卡住,请使用您所指问题的答案中提出的模型。在该模型中,EF 也将足够智能,可以尽可能与User.ContactId 短路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多