none
Renaming foreign key columns

    Question

  • Given the following model:

    public class Foo
    {
       
    public int Id { get; set;}
       
    public Bar TheBar { get; set; }
    }

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

    EF tries to generate the FK column as BarId.

    How can make it use TheBar?

     

    I've tried the following:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder
    .Entity<Foo>()
                   
    .HasOptional(x => x.TheBar)
                   
    .WithMany()
                   
    .IsIndependent()
                   
    .Map(x => x.MapKey(bar => bar.Id, "TheBar"));
    }

    But I get the following exception when trying to use the context:

    Exception has been thrown by the target of an invocation.
     
    ---> System.InvalidOperationException: Sequence contains more than one matching element

    Server stack trace:
       at
    System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
       at
    System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.IndependentAssociationMappingConfiguration`1.Configure(DbAssociationSetMapping associationSetMapping)
       at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.Configure(DbDatabaseMapping databaseMapping)
       at System.Data.Entity.ModelConfiguration.Utilities.IEnumerableExtensions.Each[T](IEnumerable`
    1 ts, Action`1 action)
       at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureAssociationMappings(DbDatabaseMapping databaseMapping)
       at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(DbEntityTypeMapping entityTypeMapping, DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
       at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
       at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo, Boolean validateModel)
       at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbConnection providerConnection)
       at System.Data.Entity.Internal.LazyInternalContext.CreateModel()
       at System.Lazy`
    1.CreateValue()

    Exception rethrown at [0]:
       at
    System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
       at
    System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.IndependentAssociationMappingConfiguration`1.Configure(DbAssociationSetMapping associationSetMapping)
       at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.Configure(DbDatabaseMapping databaseMapping)
       at System.Data.Entity.ModelConfiguration.Utilities.IEnumerableExtensions.Each[T](IEnumerable`
    1 ts, Action`1 action)
       at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureAssociationMappings(DbDatabaseMapping databaseMapping)
       at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(DbEntityTypeMapping entityTypeMapping, DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
       at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(DbDatabaseMapping databaseMapping, DbProviderManifest providerManifest)
       at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo, Boolean validateModel)
       at System.Data.Entity.ModelConfiguration.ModelBuilder.Build(DbConnection providerConnection)
       at System.Data.Entity.Internal.LazyInternalContext.CreateModel()
       at System.Lazy`
    1.CreateValue()
       at
    System.Lazy`1.LazyInitValue()
       at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
       at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
       at System.Data.Entity.Internal.Linq.InternalSet`
    1.Initialize()
       at
    System.Data.Entity.Internal.Linq.InternalSet`1.get_Provider()
       at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`
    1 source)
       
    --- End of inner exception stack trace ---
       at
    System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
       at
    System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
       at
    System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at
    System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)

     

    • Edited by diegose Monday, February 21, 2011 7:17 PM fixed property name
    Tuesday, February 15, 2011 8:51 PM

Answers

  • Hi,

    Yes, this is fixed in our current code base.

    If you make TheBar virtual then EF can perform lazy loading of the property (i.e. query the database the first time you access it).

    If you don't make it virtual then you can still load it via; ctx.Entry(myFoo).Reference(f => f.TheBar).Load();

    If you want to pull the data back in a single query you can use Include; var foos = ctx.Foos.Include(f => f.TheBar);

    ~Rowan

    • Marked as answer by diegose Thursday, February 24, 2011 1:02 AM
    Wednesday, February 23, 2011 10:56 PM

All replies

  • Hi,

    I'm not able to reproduce the error, if I run the following program then the database is correctly created with the renamed FK. I noticed in your code that you had .HasOptional(x => x.Bar) but Bar isn't a property in your model (I just assumed it was supposed to be .HasOptional(x => x.TheBar)?

    If you continue to see the error can you post up some code that reproduces it?

    using System.Data.Entity;
    using System.Data.Entity.ModelConfiguration;
    using System.Linq;
    
    namespace FkRename
    {
      public class Program
      {
        static void Main(string[] args)
        {
          using (var ctx = new TestContext())
          {
            ctx.Foos.FirstOrDefault();
          }
        }
      }
    
      public class Foo
      {
        public int Id { get; set; }
        public Bar TheBar { get; set; }
      }
    
      public class Bar
      {
        public int Id { get; set; }
      }
    
      public class TestContext : DbContext
      {
        public DbSet<Foo> Foos { get; set; }
        public DbSet<Bar> Bars { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
          modelBuilder.Entity<Foo>()
            .HasOptional(x => x.TheBar)
            .WithMany()
            .IsIndependent()
            .Map(x => x.MapKey(bar => bar.Id, "TheBar"));
        }
    
      }
    
    
    
    }
    

     

    ~Rowan 

    Monday, February 21, 2011 6:54 PM
  • OK, I have more information.

    Your assumption regarding the property name is correct (I changed it in the original message)

    I do not get that error with the original code. However, I do get it with the following change:

     

    public abstract class Entity
    {
        public int Id { getset; }
    }

    public class Foo : Entity
    {
        public Bar TheBar { getset; }
    }

    public class Bar : Entity
    {
    }

    The obvious workaround is using an interface instead. I hope that is fixed in RTM.

     

    Now... after changing that to an interface, I found out that entities are persisted correctly, but TheBar is only loaded correctly if it is virtual (otherwise, it's null)

    Is this by design?

    Monday, February 21, 2011 7:43 PM
  • Hi,

    Yes, this is fixed in our current code base.

    If you make TheBar virtual then EF can perform lazy loading of the property (i.e. query the database the first time you access it).

    If you don't make it virtual then you can still load it via; ctx.Entry(myFoo).Reference(f => f.TheBar).Load();

    If you want to pull the data back in a single query you can use Include; var foos = ctx.Foos.Include(f => f.TheBar);

    ~Rowan

    • Marked as answer by diegose Thursday, February 24, 2011 1:02 AM
    Wednesday, February 23, 2011 10:56 PM