开发者

Entity Framework Code First One-To-Many Relation Mapping with link table

开发者 https://www.devze.com 2023-03-28 17:21 出处:网络
I have a question about One-To-Many relationship between two tables when there is the link (join) table between.

I have a question about One-To-Many relationship between two tables when there is the link (join) table between.

Example tables:

ChildTable:
ID int NOT NULL PK
Relation int NOT NULL

ParentTable:
ID int NOT NULL PK
Name nvarchar(50) NOT NULL

ParentChildren:
ParentTable_ID int NOT NULL PFK
ChildTable_ID int NOT NULL PFK

Entities:

public class ChildTable
{
    public ChildTable()
    {
        this.ParentTables = new List<ParentTable>();
    }

    public int ID { get; set; }       
    public int Relation { get; set; }
    public virtual ICollection<ParentTable> ParentTables { get; set; }
}

public class ParentTable
{
    public ParentTable()
    {
        this.ChildTables = new List<ChildTable>();
    }

    public int ID { get; set; }   
    public string Name { get; set; }
    public virtual ICollection<ChildTable> ChildTables { get; set; }
}

Mapping:

public class ChildTableMap : EntityTypeConfiguration<ChildTable>
{
    public ChildTableMap()
    {
        // Primary Key
        this.HasKey(t => t.ID);

        // Properties
        // Table & Column Mappings
        this.ToTable("ChildTable");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Relation).HasColumnName("Relation");
    }
}
public class ParentTableMap : EntityTypeConfiguration<ParentTable>
{
    public ParentTableMap()
    {
        // Primary Key
        this.HasKey(t => t.ID);

        // Properties
        this.Property(t => t.Name)
            .IsRequired()
            .HasMaxLength(50);

        // Table & Column Mappings
        this.ToTable("ParentTable");
        this.Property(t => t.ID).HasColumnName("ID");
        this.Property(t => t.Name).HasColumnName("Name");

        // Relationships
        this.HasMany(t => t.ChildTables)
            .WithMany(t => t.ParentTables)
            .Map(m =>
                {
                    m.ToTable("ParentChildren");
                    m.MapLeftKey("ParentTable_ID");
                    m.MapRightKey("ChildTable_ID");
                });

    }
}

Context:

public class TestContext : DbContext
{
    static TestContext()
    {开发者_如何学Go 
        Database.SetInitializer<TestContext>(null);
    }

    public DbSet<ChildTable> ChildTables { get; set; }
    public DbSet<ParentTable> ParentTables { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
        modelBuilder.Configurations.Add(new ChildTableMap());
        modelBuilder.Configurations.Add(new ParentTableMap());
    }
}

Code to add the child and parent:

using (var context = new TestContext())
        {
                var parent = new ParentTable { Name = "Mother Goose" };                   
                var child = new ChildTable { Relation = 1 };                   
                context.ParentTables.Add(parent);
                context.ChildTables.Add(child);
                parent.ChildTables.Add(child);
                context.SaveChanges();
        }

And all works as expected, but I really have only 1 parent and many children.

How to do the mapping that includes writing to the link table (with changed ChildTable class that does not have the ICollection of ParentTable)?


It's not possible to define such a mapping. Join tables are only for many-to-many relationships. You have to keep in mind that your database schema (which you cannot change, as you said in the comments to your question) already defines a many-to-many relationship. For example these two entries...

ParentTable_ID:  1    2
ChildTable_ID:   1    1

...are valid entries in the ParentChildren table, they don't violate the PK constraint and they say that child 1 has the two parents 1 and 2.

If your business logic only allows that a child can have only one single parent, the database schema is modeled wrong. You cannot fix this flaw with EF.

A possible workaround might be to ensure in your business logic in your code that the ParentTables collection of your ChildTable entity never has more than one element, perhaps by introducing a not mapped helper property:

public class ChildTable
{
    public ChildTable()
    {
        this.ParentTables = new List<ParentTable>();
    }

    public int ID { get; set; }       
    public int Relation { get; set; }
    public virtual ICollection<ParentTable> ParentTables { get; set; }

    [NotMapped] // or use .Ignore(c => c.ParentTable) in Fluent API
    public ParentTable ParentTable
    {
        get
        {
            return ParentTables.SingleOrDefault();
            // let is crash if there is more than one element in the list
        }

        set
        {
            var oldParent = ParentTables.SingleOrDefault();
            if (oldParent != value)
            {
                ParentTables.Clear();
                if (value != null)
                    ParentTables.Add(value);
            }
        }
    }
}

It's only a little help if you only use the ParentTable property. You have to avoid to use the collection and add more than one parent. You cannot make it private because it must be mapped. It's also not safe on the other side of the relationship. You could write: parent1.ChildTables.Add(child1); parent2.ChildTables.Add(child1); which would lead to two parents for child1 in the database. You could catch this by writing helper methods for adding a child on the ParentTable entity which check if the added child doesn't already have a parent other than this parent.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号