Entity Framework Code First : Setting up One-To-One foreign key association using Annotations

I have following two Entities that I am trying to relate (one to one) using foreign key associations.

public class StandardRack {
    public int Id {get;set}
    public StandardRelay StandardRelay {get;set} 
}

public class StandardRelay {
    public int Id {get;set} 

    public int StandardRack_Id {get;set;}
    [Required][ForeignKey("StandardRack_Id")]
    public StandardRack StandardRack { get; set; }
}

This throws ModelValidationException. Any ideas why such a seemingly simple one-to-one bidirectional relationship cannot be configured.

Edit:

Here is the Exception:

System.Data.Entity.ModelConfiguration.ModelValidationException was caught Message=One or more validation errors were detected during model generation:

System.Data.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'StandardRelay_StandardRack_Source' in relationship 'StandardRelay_StandardRack'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be �*�.

Source=EntityFramework StackTrace: at System.Data.Entity.ModelConfiguration.Edm.EdmModelExtensions.ValidateAndSerializeCsdl(EdmModel model, XmlWriter writer) at System.Data.Entity.ModelConfiguration.Edm.EdmModelExtensions.ValidateCsdl(EdmModel model) at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo) at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection) at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext) at System.Data.Entity.Internal.RetryLazy2.GetValue(TInput input) at System.Data.Entity.Internal.LazyInternalContext.InitializeContext() at System.Data.Entity.Internal.InternalContext.Initialize() at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) at System.Data.Entity.Internal.Linq.InternalSet1.Initialize() at System.Data.Entity.Internal.Linq.InternalSet1.GetEnumerator() at System.Data.Entity.Infrastructure.DbQuery1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at TestApplication.MainWindow.Window_Loaded(Object sender, RoutedEventArgs e) in D:\RailwayProjects\RelayAnalysis\TestApplication\MainWindow.xaml.cs:line 33 InnerException:


Solution 1:

One-to-one foreign key associations are not supported by Entitiy Framework. You must remove the foreign key and use shared primary keys (primary key of the dependent is its foreign key to the principal at the same time):

public class StandardRack {
    public int Id {get;set}
    public StandardRelay StandardRelay {get;set} 
}

public class StandardRelay {
    public int Id {get;set} 
    public StandardRack StandardRack { get; set; }
}

Mapping in Fluent API:

modelBuilder.Entity<StandardRack>()
    .HasOptional(rack => rack.StandardRelay)
    .WithRequired(relay => relay.StandardRack);

(I'm assuing here that a StandardRack has an optional Relay.)

Solution 2:

Here is how you can specify one-to-one relationship with FK using fluent api.

Note that FK is not explicitly defined in Enitity, but it's defined using fluent api.

public class StandardRack {
    public int Id {get;set}
    public StandardRelay StandardRelay {get;set} 
}

public class StandardRelay {
    public int Id {get;set} 
    public StandardRack StandardRack { get; set; }
}


modelBuilder.Entity<StandardRack>()
            .HasOptional(x => x.StandardRelay)
            .WithOptionalPrincipal(y => y.StandardRack)
            .Map(configurationAction: new Action<ForeignKeyAssociationMappingConfiguration>(x => x.MapKey("StandardRack_Id")));

fluent api will add column StandardRack_Id in StandardRelay.

Note that method name WithOptionalPrincipal() is quite ironic. msdn documentation of WithOptionalDependent shall make it clear.