首页 > 解决方案 > Entity Framework Core:两个实体之间的多个一对多关系

问题描述

我尝试通过让翻译模型运行

public class Item
{
  public Item
  {
      TextTranslationID = Guid.NewGuid();
      DescriptionTranslationID = Guid.NewGuid();

      TextTranslations = new HashSet<Translation>();
      DescriptionTranslations = new HashSet<Translation>();
  }

  [Key]
  public int ItemID { get; set; }

  public Guid TextTranslationID { get; set; }

  public Guid DescriptionTranslationID { get; set; }

  [ForeignKey(nameof(TextTranslationID))]
  public virtual ICollection<Translation> TextTranslations { get; set; }

  [ForeignKey(nameof(DescriptionTranslationID))]
  public virtual ICollection<Translation> DescriptionTranslations { get; set; }
}

和翻译实体

public class Translation
{
  public Translation()
  {
    UniqueTranslationID = Guid.NewGuid();
  }

  [Key]
  public Guid UniqueTranslationID { get; set; }

  /// <summary>
  /// The translation id, keyed with the language.
  /// </summary>
  [Required]
  public Guid TranslationID { get; set; }

  /// <summary>
  /// The 2-char language code. eg "en", "es"
  /// </summary>
  [Required]
  [StringLength(2, MinimumLength = 2)]
  public string Language { get; set; }

  [Required]
  [StringLength(2000)]
  public string Text { get; set; }
}

这是一个单向关系,因此我不需要也不想要 Translation.Parent 或 Translation 实体上的类似内容。实体只是翻译的Item众多消费者之一,因此这里不需要逆属性。如您所见,该项目与翻译有两个连接。

我已经尝试了很多与模型构建器的组合来在 sql 中实现这样一个简单的任务,但是生成的 sql 脚本总是想在翻译表中添加一个 DescriptionTranslationID 和 TextTranslationID。

...

migrationBuilder.CreateTable(
    name: "Translations",
    columns: table => new
    {
        UniqueTranslationID = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
        TranslationID = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
        Language = table.Column<string>(type: "nvarchar(2)", maxLength: 2, nullable: false),
        Text = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false),
        DescriptionTranslationID = table.Column<int>(type: "int", nullable: true),
        TextTranslationID = table.Column<int>(type: "int", nullable: true)
    },

...

如何设置从项目到翻译的两个一对多关系?非常感谢!

标签: c#entity-framework-core

解决方案


这是解决方案的完整示例。
给定两个实体(问题、答案),它们需要多个本地化(Localization)而不需要反向导航。

public class CatalogQuestion
{
  public CatalogQuestion()
  {
    TextTranslationID = Guid.NewGuid();
    DescriptionTranslationID = Guid.NewGuid();

    TextTranslations = new HashSet<Translation>();
    DescriptionTranslations = new HashSet<Translation>();
  }

  [Key]
  public int CatalogQuestionID { get; set; }
  public Guid TextTranslationID { get; set; }
  public Guid DescriptionTranslationID { get; set; }

  ///

  public virtual ICollection<Translation> TextTranslations { get; set; }

  public virtual ICollection<Translation> DescriptionTranslations { get; set; }
}

public class CatalogAnswer
{
  public CatalogAnswer()
  {
    TextTranslationID = Guid.NewGuid();
    DescriptionTranslationID = Guid.NewGuid();

    TextTranslations = new HashSet<Translation>();
    DescriptionTranslations = new HashSet<Translation>();
  }

  [Key]
  public int CatalogAnswerID { get; set; }
  public Guid TextTranslationID { get; set; }
  public Guid DescriptionTranslationID { get; set; }
  
  ///

  public virtual ICollection<Translation> TextTranslations { get; set; }

  public virtual ICollection<Translation> DescriptionTranslations { get; set; }
}

/// <summary>
/// Allows every entity that has a <see cref="Guid"/> to have a translation.
/// </summary>
public class Translation
{
  public Translation()
  {
    UniqueTranslationID = Guid.NewGuid();
  }

  /// <summary>
  /// Used for direct addressing this single translation.
  /// </summary>
  [Key]
  public Guid UniqueTranslationID { get; set; }

  /// <summary>
  /// The translation id, keyed with the language.
  /// Must not be an empty guid.
  /// </summary>
  [Required]
  public Guid TranslationID { get; set; }

  /// <summary>
  /// The 2-char language code. eg "de", "en"
  /// </summary>
  [Required]
  [StringLength(2, MinimumLength = 2)]
  public string Language { get; set; }
  
  [Required]
  [StringLength(2000)]
  public string Text { get; set; }
}

这是模型构建器的 FluentApi,将这些东西粘合在一起:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Translation>(entity => {
      entity.HasIndex(e => new { e.TranslationID, e.Language }).IsUnique();
    });

    modelBuilder.Entity<CatalogQuestion>(entity => {
      entity.HasMany(e => e.TextTranslations).WithOne().HasForeignKey(e => e.TranslationID).HasPrincipalKey(e => e.TextTranslationID).HasConstraintName("FK_Translations_CatalogQuestionsText_TranslationID");
      entity.HasMany(e => e.DescriptionTranslations).WithOne().HasForeignKey(e => e.TranslationID).HasPrincipalKey(e => e.DescriptionTranslationID).HasConstraintName("FK_Translations_CatalogQuestionsDescription_TranslationID");
    });

    modelBuilder.Entity<CatalogAnswer>(entity => {
      entity.HasMany(e => e.TextTranslations).WithOne().HasForeignKey(e => e.TranslationID).HasPrincipalKey(e => e.TextTranslationID).HasConstraintName("FK_Translations_CatalogAnswersText_TranslationID");
      entity.HasMany(e => e.DescriptionTranslations).WithOne().HasForeignKey(e => e.TranslationID).HasPrincipalKey(e => e.DescriptionTranslationID).HasConstraintName("FK_Translations_CatalogAnswersDescription_TranslationID");
    });
}

由于我们正在创建两个非主键之间的关系,因此我们需要在 FlientApi 中定义它,否则迁移将与注释和 FluentApi 混淆。


推荐阅读