简体   繁体   中英

Entity Framework (Code First) one to many - generates extra fields instead of using mapped keys

I am attempting to get some nested data using Fluent API and .Include(). The database I am working with is not normalised, and I do not have control over it.

"Parent" table is TASK, and "child" table is STOCK_MOVEMENT, with a "many stock movements to one task" relation.

The mapping is supposed to be from TASK, with its composite key { "TSK_SITE", "TSK_TYPE" } onto STOCK_MOVEMENT, with its composite key { "MOV_SITE", "MOV_TYPE", "MOV_MOVE" }.

The article here seemed to be most appropriate to my task at hand, particularly the "Configuring a Composite Foreign Key" section. Unfortunately, I don't seem to get the expected fields prompted by my intellisense for the "HasForeignKey" line, so I had to try something else... (thoughts welcome!)

What I tried instead is this:

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

Tried both "HasRequired" and "HasOptional"... However, the SQL that ultimately gets generated by EF contains extra fields instead of mapping against the Foreign Key that I have specified.

Can anybody explain how I should change my mappings in order to get correct SQL, please? Guiding questions are also very welcome.

I am attaching all the relevant code below...

Many thanks!

Victoria

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映射的外键不正确


To clarify on the database fields and tables involved: I only mentioned columns used as keys to simplify the problem:

1) "TASK" table contains columns "TSK_SITE" and "TSK_TYPE", which constitute its composite key (+ more columns).

2) "STOCK_MOVEMENT" table contains columns "MOV_SITE", "MOVE_TYPE" and "MOV_MOVE", which constitute part of its composite key (+ more columns).. I have not used "MOV_MOVE" in my mapping, as there is no equivalent column in the parent TASK table.

I am expected to retrieve one or more STOCK_MOVEMENT records per TASK record.

The mapping of TestTask seems correct. For the mapping of TestMovement you must map the full key with all three parts:

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

Then map the column names and set all three key properties to required (key columns can't be nullable):

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();

Then - most important - don't use MapKey for your relationship mapping because your foreign key columns are properties in the model class. In that case you always must use HasForeignKey :

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

I know that you said that for HasForeignKey Intellisense didn't offer the expected properties (but does the code compile anyway?). That's another problem you need to solve. But using MapKey is no solution and doesn't bring you nowhere for your model and mapping.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM