【问题标题】:One to Many with a join table and an optional relationship in Entity Framework 4.1 Fluent APIEntity Framework 4.1 Fluent API 中带有连接表和可选关系的一对多
【发布时间】:2011-08-11 23:41:24
【问题描述】:

再次使用无法更改的旧数据库并使用带有 Fluent API 的 Entity Framework 4.1 仅读取数据。

public class Client
{
  [Key]
  public int ClientID { get; set; }
  public string Name { get; set ;}

  public virtual ICollection<Phone> Phones { get; set; }
}

public class Phone
{
  [Key]
  public int PhoneID { get; set; }
  public string Number { get; set; }

  public virtual Client Client { get; set; }
}

public class ClientPhone
{
  [Key]
  [Column(Order=0)]
  public int ClientID { get; set; }

  [Key]
  [Column(Order=1)]
  public int PhoneID { get; set; }
}

我希望客户端有很多电话,但电话应该只有一个可选的客户端。 注意:电话应该只有 0|1 个客户端。我不想要多对多。 所以我尝试了以下方法:

modelBuilder.Entity<Client>()
  .HasMany(c => c.Phones)
  .WithOptional(p => p.Client)
  .Map(m =>
    {
      m.MapKey("ClientID");
      m.ToTable("ClientPhone");
    });

modelBuilder.Entity<Phone>()
  .HasOptional(p => p.Client)
  .WithMany(c => c.Phones)
  .Map(m =>
    {
      m.MapKey("PhoneID");
      m.ToTable("ClientPhone");
    });

我尝试了几种排列,通常会收到关于“类型中的每个属性名称必须是唯一的”的错误。

感谢您的帮助。

用答案编辑

这是我对实体类所做的修改。可以从一个客户端导航到多个电话以及从一个电话到一个客户端,但您必须通过 ClientPhone 连接表。

[Table("Client")]
public class Client
{
  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public int ClientID { get; set; }

  public string Name { get; set ;}

  public virtual ICollection<Phone> Phones { get; set; } // Client has * Phones
}

[Table("Phone")]
public class Phone
{
  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public int PhoneID { get; set; }

  public string Number { get; set; }

  public virtual Client Client { get; set; } // Phone has 0|1 Client
}

[Table("ClientPhone")]
public class ClientPhone
{
  // Removed the Key attribute
  public int ClientID { get; set; }

  [Key] // Left the Key on the 0|1 side
  [ForeignKey("Phone")]
  public int PhoneID { get; set; }

  public virtual Client Client { get; set; } // One Client
  public virtual Phone Phone { get; set; } // One Phone
}

【问题讨论】:

  • 您的表是如何定义的?数据库中有哪些表,配置了哪些关系?如果数据库包含多对多和联结表,则不能将其映射为一对多。

标签: entity-framework entity-framework-4.1 ef-code-first


【解决方案1】:

这里不需要两次指定电话和客户端的关系。只用一个这样的。

modelBuilder.Entity<Client>().HasMany(c => c.Phones).WithMany(i => i.Clients)
                .Map(t => t.MapLeftKey("PhoneID")
                    .MapRightKey("ClientID")
                    .ToTable("ClientPhone"));

您不需要模型来映射“ClientPhone”之类的关系表,因为 EF 会处理它。如果您有旧数据库,则可以像我的回答一样自定义映射。正如@Ladislav Mrnka 所说,对于数据库级别的一对多关系,您不想为该关系保留一个单独的表。为此,

public class Client
{
  [Key]
  public int ClientID { get; set; }
  public string Name { get; set ;}

  public virtual ICollection<Phone> Phones { get; set; }
}

public class Phone
{
  [Key]
  public int PhoneID { get; set; }
  public string Number { get; set; }

  public virtual Client Client { get; set; }
}

这对 EF 来说已经足够了。它将在 Phone 表中创建两个具有 clientId 列的表以保持关系。

问题是您的旧数据库中有单独的关系表。我认为旧数据库是为多对多关系定义的。再次考虑您的域逻辑。

如果您想保持一对多关系,我认为您可以在域级别的多对多关系中定义(电话有很多客户,客户在模型类中有很多电话)并在您的业务层添加电话只保留一个客户端的验证。

【讨论】:

  • 我将问题更新为更明确...我需要电话只有 0|1 个客户端。
  • 感谢 Jayantha 的详尽回答。在这个和@Ladislav Mrnka 的答案之间,答案是完整的。我相信我发布的解决方案适用于我的情况,但您的答案仍然是可能的。
【解决方案2】:

一对多关系没有连接(联结)表。如果您的数据库使用联结表将客户端连接到电话,则您必须使用多对多关系(电话可以有多个客户端),如 @Jayantha 所述。

【讨论】:

  • 我想我已经根据我的场景让它工作了,这个问题并不完全清楚。您可以使用连接表在一侧进行一对多,在另一侧进行一对一,这就是我想要做的。
  • 在这种情况下,联结表是完全多余的。
  • 我添加了我的解决方案,很高兴了解如何消除冗余并改进我的尝试。
猜你喜欢
  • 2013-08-16
  • 2013-01-26
  • 2011-11-03
  • 2021-01-19
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 1970-01-01
  • 2023-01-25
相关资源
最近更新 更多