Entity Framework (Code First) один ко многим — создает дополнительные поля вместо использования сопоставленных ключей

Я пытаюсь получить некоторые вложенные данные, используя Fluent API и .Include(). База данных, с которой я работаю, не нормализована, и я не могу ее контролировать.

«Родительская» таблица — это TASK, а «дочерняя» таблица — это STOCK_MOVEMENT с отношением «много движений запасов к одной задаче».

Предполагается, что сопоставление должно быть из TASK с его составным ключом {"TSK_SITE", "TSK_TYPE"} на STOCK_MOVEMENT с его составным ключом {"MOV_SITE", "MOV_TYPE", "MOV_MOVE"}.

статья здесь показалась наиболее подходящей для моей задачи, особенно раздел «Настройка составного внешнего ключа». К сожалению, я, кажется, не получаю ожидаемые поля, подсказанные моим intellisense для строки «HasForeignKey», поэтому мне пришлось попробовать что-то еще... (мысли приветствуются!)

Вместо этого я пробовал следующее:

            this.HasRequired(sm => sm.TestTask)
            .WithMany(t => t.Movements)
            .Map(m => m.MapKey("MOV_SITE", "MOV_TYPE"));

Пробовал как "HasRequired", так и "HasOptional"... Однако SQL, который в конечном итоге генерируется EF, содержит дополнительные поля вместо сопоставления с указанным мной внешним ключом.

Кто-нибудь может объяснить, как мне изменить сопоставления, чтобы получить правильный SQL? Наводящие вопросы также очень приветствуются.

Я прилагаю весь соответствующий код ниже...

Большое спасибо!

Виктория

public class TestTask : IMyObject
{
    public TestTask()
    {
        this.KeyFields = new string[] { "Site", "TaskType" };

        this.Movements = new HashSet<TestMovement>();
    }

    public string Site { get; set; }
    public string TaskType { get; set; }
    public string Key { get; set; }

    public virtual HashSet<TestMovement> Movements { get; set; }

    //... more code
}

internal partial class TestTask_Mapping : EntityTypeConfiguration<TestTask>
{
    public TestTask_Mapping()
    {
        this.HasKey(t => new { t.Site, t.TaskType });

        this.ToTable("TASK");

        this.Property(t => t.Site)
            .HasColumnName("TSK_SITE")
            .IsRequired();

        this.Property(t => t.TaskType)
            .HasColumnName("TSK_TYPE")
            .IsRequired();

        this.Property(t => t.Key)
            .HasColumnName("TSK_REF");
    }
}

public class TestMovement : IMyObject
{
    public TestMovement()
    {
        this.KeyFields = new string[] { "Site", "MoveType" };
    }

    public string Site { get; set; }
    public string MoveType { get; set; }
    public string Key { get; set; }

    public virtual TestTask TestTask { get; set; }

    // ... more code
}

public class TestStockMovement_Mapping : EntityTypeConfiguration<TestMovement>
{
    public TestStockMovement_Mapping()
    {
        this.HasKey(t => new { t.Site, t.MoveType });

        this.HasRequired(sm => sm.TestTask)
            .WithMany(t => t.Movements)
            .Map(m => m.MapKey("MOV_SITE", "MOV_TYPE"));

        this.Property(t => t.Site)
            .IsRequired();

        this.Property(t => t.MoveType)
            .IsRequired();

        this.ToTable("STOCK_MOVEMENT");

        //this.Property(t => t.Site).HasColumnName("MOV_SITE");
        //this.Property(t => t.MoveType).HasColumnName("MOV_TYPE");
        this.Property(t => t.Key).HasColumnName("MOV_MOVE");

    }
}

// In DbContext...

public DbSet<TestTask> TestTasks { get; set; }
public DbSet<TestMovement> TestStockMovements { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new TestTask_Mapping());
    modelBuilder.Configurations.Add(new TestStockMovement_Mapping());
}

// In the appropriate method in the repository ...

IQueryable<TestTask> testTasks = context.TestTasks.Include(t => t.Movements);

// Where testTasks results in the SQL with incorrect foreign key field references 
// (please see the picture that tells a thousand words below)...

Неправильный внешний ключ, сопоставленный EF


Чтобы уточнить задействованные поля и таблицы базы данных: я упомянул только столбцы, используемые в качестве ключей, чтобы упростить проблему:

1) Таблица "TASK" содержит столбцы "TSK_SITE" и "TSK_TYPE", составляющие ее составной ключ (+ еще столбцы).

2) Таблица "STOCK_MOVEMENT" содержит столбцы "MOV_SITE", "MOVE_TYPE" и "MOV_MOVE", которые составляют часть ее составного ключа (+ дополнительные столбцы). Я не использовал "MOV_MOVE" в своем отображении, так как нет эквивалента столбец в родительской таблице TASK.

Я должен получить одну или несколько записей STOCK_MOVEMENT для каждой записи TASK.


person Victoria    schedule 30.09.2014    source источник
comment
Если { "MOV_SITE", "MOV_TYPE" } является вашим первичным ключом в таблице STOCK_MOVEMENT, что тогда является внешним ключом для таблицы TASK? Это не может быть { "MOV_SITE", "MOV_TYPE" } снова (как говорит ваше сопоставление), если отношение один ко многим. Если PK и FK идентичны, это может быть только отношение один к одному.   -  person Slauma    schedule 30.09.2014
comment
Большое спасибо за ответ на мой вопрос, Слаума! Я добавил правку в свой исходный пост, пояснив, что в таблице STOCK_MOVEMENT есть еще один столбец, который является частью составного ключа для этой таблицы, но который я не добавлял в сопоставленную связь. В родительской таблице TASK нет эквивалента этому столбцу, поэтому сопоставлять не с чем. Как я могу (должен ли я) учитывать этот столбец в моем коде, чтобы создать один ко многим?...   -  person Victoria    schedule 30.09.2014


Ответы (1)


Отображение TestTask кажется правильным. Для сопоставления TestMovement вы должны сопоставить полный ключ со всеми тремя частями:

this.HasKey(t => new { t.Site, t.MoveType, t.Key });

Затем сопоставьте имена столбцов и установите для всех трех ключевых свойств значение required (ключевые столбцы не могут иметь значение NULL):

this.Property(t => t.Site)
    .HasColumnName("MOV_SITE")
    .IsRequired();

this.Property(t => t.MoveType)
    .HasColumnName("MOV_TYPE")
    .IsRequired();

this.Property(t => t.Key)
    .HasColumnName("MOV_MOVE")
    .IsRequired();

Затем, что наиболее важно, не используйте MapKey для отображения отношений, потому что ваши столбцы внешнего ключа являются свойствами в классе модели. В этом случае вы всегда должны использовать HasForeignKey:

this.HasRequired(sm => sm.TestTask)
    .WithMany(t => t.Movements)
    .HasForeignKey(t => new { t.Site, t.MoveType });

Я знаю, вы сказали, что для HasForeignKey Intellisense не предлагает ожидаемых свойств (но компилируется ли код в любом случае?). Это еще одна проблема, которую вам нужно решить. Но использование MapKey не является решением и никуда не приведет вашу модель и сопоставление.

person Slauma    schedule 30.09.2014
comment
Спасибо за подробную инструкцию, Слаума, попробую через несколько часов... - person Victoria; 01.10.2014
comment
Начали с нуля, и теперь IntelliSense работает, и ваши советы точны! Спасибо за помощь, все работает! - person Victoria; 03.10.2014