Iskander Raimbayev Iskander Raimbayev - 2 months ago 9
C# Question

Entity Framework : should I provide double-side relationship configuration?

My question is very simple : here I have two classes with one-to-many relationship.
Let's take a sample :
Sample taken from book of Julia Lerman "Programming Entity Framework : Code First"

public class Destination
{
public int DestinationId { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public string Description { get; set; }
public byte[] Photo { get; set; }
public ICollection<Lodging> Lodgings { get; set; }

public Destination()
{
Lodgings = new List<Lodging>();
}
}

public class Lodging
{
public int LodgingId { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
public bool IsResort { get; set; }
public decimal MilesFromNearestAirport { get; set; }
public Destination Destination { get; set; }
public int DestinationId { get; set; }
}


And here configurations with FluentApi :

public class LodgingConfiguration : EntityTypeConfiguration<Lodging>
{
public LodgingConfiguration()
{
Property(p => p.Name).IsRequired().HasMaxLength(200);
HasRequired(p => p.Destination).WithMany(p => p.Lodgings);
}
}

public class DestinationConfiguration : EntityTypeConfiguration<Destination>
{
public DestinationConfiguration()
{
Property(p => p.Name).IsRequired().HasMaxLength(100);
Property(p => p.Description).HasMaxLength(500);
Property(p => p.Photo).HasColumnType("image");
HasMany(p=>p.Lodgings).WithRequired(l=>l.Destination);
}
}


I suppose that lines

HasRequired(p => p.Destination).WithMany(p => p.Lodgings);


and

HasMany(p=>p.Lodgings).WithRequired(l=>l.Destination);


provide the same result on relationships between Destination and Lodging.

If I define only one of this rules, it works well too.
Is it a good practice to define the same rule on both sides or is one-side declaration is Ok?

Answer

Here are the results if you do add-migration for the three variants:

With mappings in Lodging and Destination Configuration Types

public override void Up()
{
    CreateTable(
        "dbo.Lodgings",
        c => new
            {
                LodgingId = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 200),
                Owner = c.String(),
                IsResort = c.Boolean(nullable: false),
                MilesFromNearestAirport = c.Decimal(nullable: false, precision: 18, scale: 2),
                Destination_DestinationId = c.Int(nullable: false),
            })
        .PrimaryKey(t => t.LodgingId)
        .ForeignKey("dbo.Destinations", t => t.Destination_DestinationId, cascadeDelete: true)
        .Index(t => t.Destination_DestinationId);

    CreateTable(
        "dbo.Destinations",
        c => new
            {
                DestinationId = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 100),
                Country = c.String(),
                Description = c.String(maxLength: 500),
                Photo = c.Binary(storeType: "image"),
            })
        .PrimaryKey(t => t.DestinationId);

}

Removing mapping from Lodging Configuration Type

public override void Up()
{
    CreateTable(
        "dbo.Lodgings",
        c => new
            {
                LodgingId = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 200),
                Owner = c.String(),
                IsResort = c.Boolean(nullable: false),
                MilesFromNearestAirport = c.Decimal(nullable: false, precision: 18, scale: 2),
                Destination_DestinationId = c.Int(nullable: false),
            })
        .PrimaryKey(t => t.LodgingId)
        .ForeignKey("dbo.Destinations", t => t.Destination_DestinationId, cascadeDelete: true)
        .Index(t => t.Destination_DestinationId);

    CreateTable(
        "dbo.Destinations",
        c => new
            {
                DestinationId = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 100),
                Country = c.String(),
                Description = c.String(maxLength: 500),
                Photo = c.Binary(storeType: "image"),
            })
        .PrimaryKey(t => t.DestinationId);

}

Removing mapping from Destination Configuration Type

public override void Up()
{
    CreateTable(
        "dbo.Lodgings",
        c => new
            {
                LodgingId = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 200),
                Owner = c.String(),
                IsResort = c.Boolean(nullable: false),
                MilesFromNearestAirport = c.Decimal(nullable: false, precision: 18, scale: 2),
                Destination_DestinationId = c.Int(nullable: false),
            })
        .PrimaryKey(t => t.LodgingId)
        .ForeignKey("dbo.Destinations", t => t.Destination_DestinationId, cascadeDelete: true)
        .Index(t => t.Destination_DestinationId);

    CreateTable(
        "dbo.Destinations",
        c => new
            {
                DestinationId = c.Int(nullable: false, identity: true),
                Name = c.String(nullable: false, maxLength: 100),
                Country = c.String(),
                Description = c.String(maxLength: 500),
                Photo = c.Binary(storeType: "image"),
            })
        .PrimaryKey(t => t.DestinationId);

}

As you can see, the generated code (Sql server in this case) is exactly the same. It's always a matter of style and to be consistent. You could explicitly set the mappings for the children in the parent configuration type or any other styles.

Here is a very good answer about this topic.

Comments