Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF 4.1 Code First ModelBuilder HasForeignKey for One to One Relationships

Very simply I am using Entity Framework 4.1 code first and I would like to replace my [ForeignKey(..)] attributes with fluent calls on modelBuilder instead. Something similar to WithRequired(..) and HasForeignKey(..) below which tie an explicit foreign key property (CreatedBySessionId) together with the associated navigation property (CreatedBySession). But I would like to do this for a one to one relationsip instead of a one to many:

modelBuilder.Entity<..>().HasMany(..).WithRequired(x => x.CreatedBySession).HasForeignKey(x => x.CreatedBySessionId)

A more concrete example is below. This works quite happily with the [ForeignKey(..)] attribute but I'd like to do away with it and configure it purely on modelbuilder.

public class VendorApplication
{
    public int VendorApplicationId { get; set; }

    public int CreatedBySessionId { get; set; }
    public virtual Session CreatedBySession { get; set; }
}

public class Session
{
    public int SessionId { get; set; }

    [ForeignKey("CurrentApplication")]
    public int? CurrentApplicationId { get; set; }
    public virtual VendorApplication CurrentApplication { get; set; }

    public virtual ICollection<VendorApplication> Applications { get; set; }
}

public class MyDataContext: DbContext
{
    public IDbSet<VendorApplication> Applications { get; set; }
    public IDbSet<Session> Sessions { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Session>().HasMany(x => x.Applications).WithRequired(x => x.CreatedBySession).HasForeignKey(x => x.CreatedBySessionId).WillCascadeOnDelete(false); 
        // Note: We have to  turn off Cascade delete on Session <-> VendorApplication relationship so that SQL doesn't complain about cyclic cascading deletes
    }
}

Here a Session can be responsible for creating many VendorApplications (Session.Applications), but a Session is working on at most one VendorApplication at a time (Session.CurrentApplication). I would like to tie the CurrentApplicationId property with the CurrentApplication navigation property in modelBuilder instead of via the [ForeignKey(..)] attribute.

Things I've Tried

When you remove the [ForeignKey(..)] attribute the CurrentApplication property generates a CurrentApplication_VendorApplicationId column in the database which is not tied to the CurrentApplicationId column.

I've tried explicitly mapping the relationship using the CurrentApplicationId column name as below, but obviously this generates an error because the database column name "CurrentApplicationId" is already being used by the property Session.CurrentApplicationId:

modelBuilder.Entity<Session>().HasOptional(x => x.CurrentApplication).WithOptionalDependent().Map(config => config.MapKey("CurrentApplicationId"));

It feels like I'm missing something very obvious here since all I want to do is perform the same operation that [ForeignKey(..)] does but within the model builder. Or is it a case that this is bad practise and was explicitly left out?

like image 498
Walter Avatar asked Jul 14 '12 00:07

Walter


1 Answers

You need to map the relationship as one-to-many and omit the collection property in the relationship.

modelBuilder.Entity<Session>()
   .HasOptional(x => x.CurrentApplication)
   .WithMany()
   .HasForeignKey(x => x.CurrentApplicationId)
like image 88
Eranga Avatar answered Sep 22 '22 15:09

Eranga