#c# #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; }
}
Это отношения в одном направлении, поэтому мне не нужен и не нужен перевод.Родитель или что — то подобное в сущности перевода.
Item
Сущность является лишь одним из многих потребителей перевода, поэтому здесь не требуется обратное свойство.
Как вы можете видеть, этот элемент имеет две связи с переводами.
Я уже перепробовал так много комбинаций с modelbuilder, чтобы выполнить такую простую задачу в sql, но сгенерированный сценарий sql всегда хочет добавить идентификатор перевода описания и идентификатор перевода текста в таблицу перевода.
...
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)
},
...
Как я могу настроить два отношения «один ко многим» от элемента к переводу?
Большое спасибо!
Ответ №1:
Вот полный пример с решением.
Даны две сущности (Вопрос, ответ), которым требуется несколько локализаций (Локализация) без необходимости обратной навигации.
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.