Search code examples
entity-frameworkentity-framework-4.1ef-code-firstfluent-interface

Entity Framework 4.1 Code First - How to create two different relationships between two tables


I need to create a relationship where I have a user table that links to an address table. The problem is that I need the address table to also store historic addresses. It is also possible that a user might not have an address at all.

public class user
{
  public virtual int ID { get; set; }
  ...
  public virtual int? AddressId { get; set; }

  [ForeignKey("AddressId")]
  public virtual Address CurrentAddress { get; set; }

  public virtual ICollection<Address> HistoricAddresses { get; set; }
}

public class Address
{
  public virtual int ID { get; set; }
  ...
}

I tried various ways to get this to work and got various errors like putting another table between User and Address:

public class HistoricAddress
{
  public virtual int ID { get; set; }
  public Address HistoricAddress { get; set; }
}

public class user
{
  public virtual int ID { get; set; }
  public virtual Address CurrentAddress { get; set; }
  public virtual ICollection<HistricAddress> HistoricAddresses { get; set; }
  ...
}

and various other ways, but this also throws up errors. There must be a proper way of doing this. The last error I got was:

"System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'."


Solution

  • You can map the following model with 3 tables.

    public class user
    {
      public virtual int ID { get; set; }
      ...
      public virtual int? AddressId { get; set; }
    
      public virtual Address CurrentAddress { get; set; }
    
      public virtual ICollection<Address> HistoricAddresses { get; set; }
    }
    
    public class Address
    {
      public virtual int ID { get; set; }
    
      public virtual ICollection<User> Users { get; set; }
      ...
    }
    

    Override the OnModelCreating method of your custom DbContext

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .HasOptional(user => user.CurrentAddress).WithMany()
            .HasForeignKey(user => user.AddressId);
    
        modelBuilder.Entity<User>()
            .HasMany(user => user.HistoricAddresses)
            .WithMany(address => address.Users)
                .Map(m =>
                {
                    m.ToTable("UserAddresses");
                    m.MapLeftKey("UserId");
                    m.MapRightKey("AddressId");
                });
    }
    

    Tables created are

    • Users
    • Addresses
    • UserAddresses