Entity Framework Code-first Отношение многих ко многим в одной таблице - PullRequest
3 голосов
/ 31 мая 2019

У меня здесь немного странный сценарий, у меня есть одна сущность и одна таблица много-много.Следующие таблицы выглядят примерно так:

AssetHeader.cs

[Table("AssetHeader", Schema = "assets")]
public class AssetHeader
{
    public Guid Id { get; set; }

    public virtual ICollection<AssetHeaderEquipment> AssetHeaders { get; set; }

    public virtual ICollection<AssetHeaderEquipment> AssetEquipment { get; set; }
}

AssetHeaderEquipment.cs (много-ко-многим)

[Table("AssetHeaderEquipment", Schema = "assets")]
public class AssetHeaderEquipment
{
    [Key, Column(Order = 0)]
    public Guid AssetHeaderId { get; set; }
    [Key, Column(Order = 1)]
    public Guid AssetEquipmentId { get; set; }

    public virtual AssetHeader AssetHeader { get; set; }
    public virtual AssetHeader AssetEquipment { get; set; }

    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }

    [MaxLength(255)]
    public string Comment { get; set; }
}

Когда я пытаюсь создатьБД с миграцией в первую очередь кода Я получаю следующее в SQL: enter image description here

Как получить код в первую очередь для генерации правильной структуры БД, я рад использовать любой атрибутили свободно в dbcontext.

ОБНОВЛЕНИЕ: После ответа от Ивана Стоева структура соответствует ожидаемой.Однако при настройке полей теперь происходит следующее ...

Модель пытается сохранить: enter image description here

Фактический результат сохранения: enter image description here

Как AssetEquipmentId устанавливается на AssetHeaderId?

Я создал очень легкий проект dojo для полной видимости кода.Вы можете найти это здесь: GitHub Code Repo

Ответы [ 2 ]

3 голосов
/ 01 июня 2019

Это один из случаев, когда EF не может автоматически связать свойства навигации отношений.

Обычно его можно разрешить с помощью атрибутов InverseProperty или Has / With свободный API. Но поскольку этот тип отношений всегда приводит к проблеме «нескольких каскадных путей», которая требует отключения каскадного удаления по крайней мере для одного из отношений, и это может быть сделано только с помощью свободного API, нет другого выбора, кроме как использование свободного API (который для отношений ИМХО все равно лучше).

Минимум - настроить отношения, для которых вы хотите отключить каскадное удаление. Как только вы это сделаете, EF сделает все остальное.

Например:

modelBuilder.Entity<AssetHeader>()
    .HasMany(e => e.AssetEquipment)
    .WithRequired(e => e.AssetEquipment)
    .WillCascadeOnDelete(false);

Результат:

CreateTable(
    "assets.AssetHeaderEquipment",
    c => new
        {
            AssetHeaderId = c.Guid(nullable: false),
            AssetEquipmentId = c.Guid(nullable: false),
            StartDate = c.DateTime(nullable: false),
            EndDate = c.DateTime(),
            Comment = c.String(maxLength: 255),
        })
    .PrimaryKey(t => new { t.AssetHeaderId, t.AssetEquipmentId })
    .ForeignKey("assets.AssetHeader", t => t.AssetEquipmentId)
    .ForeignKey("assets.AssetHeader", t => t.AssetHeaderId, cascadeDelete: true)
    .Index(t => t.AssetHeaderId)
    .Index(t => t.AssetEquipmentId);

CreateTable(
    "assets.AssetHeader",
    c => new
        {
            Id = c.Guid(nullable: false),
        })
    .PrimaryKey(t => t.Id);

Обновление В зависимости от проблемного варианта использования правильные пары должны быть:

Collection       Inverse          FK
==========       =========        ==============
AssetHeaders     AssetEquipment   AssetEquipmentId
AssetEquipment   AssetHeader      AssetHeaderId

Следовательно, удалите вводящие в заблуждение атрибуты ForeignKey и используйте следующую полностью явную свободную конфигурацию:

modelBuilder.Entity<AssetHeader>()
    .HasMany(e => e.AssetHeaders)
    .WithRequired(e => e.AssetEquipment)
    .HasForeignKey(e => e.AssetEquipmentId)
    .WillCascadeOnDelete(false);

modelBuilder.Entity<AssetHeader>()
    .HasMany(e => e.AssetEquipment)
    .WithRequired(e => e.AssetHeader)
    .HasForeignKey(e => e.AssetHeaderId)
    .WillCascadeOnDelete(true);
0 голосов
/ 31 мая 2019

Вы можете использовать IEntityTypeConfiguration интерфейс в вашей бизнес-модели и настроить внешние ключи.

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class AssetHeaderEquipment : IEntityTypeConfiguration<AssetHeaderEquipment>
{
    public Guid AssetHeaderId { get; set; }
    public virtual AssetHeader AssetHeader { get; set; }

    public Guid AssetEquipmentId { get; set; }
    public virtual AssetHeader AssetEquipment { get; set; }

    //other properties

    public void Configure(EntityTypeBuilder<AssetHeaderEquipment> builder)
    {
        builder.HasKey(x => new { x.AssetHeaderId, x.AssetEquipmentId });
        builder.HasOne(x => x.AssetHeader).WithMany(x => x.AssetHeaders).HasForeignKey(x => x.AssetHeaderId);
        builder.HasOne(x => x.AssetEquipment).WithMany(x => x.AssetEquipment).HasForeignKey(x => x.AssetEquipmentId);
    }
}

И затем добавить следующую строку внутрь OnModelCreating метода:

builder.ApplyConfiguration(new AssetHeaderEquipment());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...