Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I remove underscore of foreign key fields in code first by convention

I've got multiple classes (including TPT) in my project. Each POCO has a BaseClass, which has a GUID (called GlobalKey) as primary key.

First I used DataAnnotations to create correct foreign keys. But then I've got problems synchronizing the corresponding GUID with the object itself.

Now I want to have only one virtual navigation property so that the GUID field in the database is created by NamingConvention. But the field name always adds an underscore followed by the word GlobalKey (which is right). When I want to remove the underscore, I don't want to go thru all my POCOs in the fluent API to do this:

// Remove underscore from Navigation-Field      modelBuilder.Entity<Person>()             .HasOptional(x => x.Address)             .WithMany()             .Map(a => a.MapKey("AddressGlobalKey")); 

Any ideas to do this for all POCOS by overwriting a convention?

Thanks in advance.

Andreas

like image 629
Andreas Geier Avatar asked Mar 28 '13 14:03

Andreas Geier


2 Answers

I finally found an answer for this, by writing a custom convention. This convention works in EF 6.0 RC1 (code from last week), so I think it's likely to continue to work after EF 6.0 is released.

With this approach, the standard EF conventions identify the independent associations (IAs), and then create the EdmProperty for the foreign key field. Then this convention comes along and renames the foreign key fields.

/// <summary> /// Provides a convention for fixing the independent association (IA) foreign key column names. /// </summary> public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType> {      public void Apply(AssociationType association, DbModel model)     {         // Identify a ForeignKey properties (including IAs)         if (association.IsForeignKey)         {             // rename FK columns             var constraint = association.Constraint;             if (DoPropertiesHaveDefaultNames(constraint.FromProperties, constraint.ToRole.Name, constraint.ToProperties))             {                 NormalizeForeignKeyProperties(constraint.FromProperties);             }             if (DoPropertiesHaveDefaultNames(constraint.ToProperties, constraint.FromRole.Name, constraint.FromProperties))             {                 NormalizeForeignKeyProperties(constraint.ToProperties);             }         }     }      private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties)     {         if (properties.Count != otherEndProperties.Count)         {             return false;         }          for (int i = 0; i < properties.Count; ++i)         {             if (!properties[i].Name.EndsWith("_" + otherEndProperties[i].Name))             {                 return false;             }         }         return true;     }      private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties)     {         for (int i = 0; i < properties.Count; ++i)         {             string defaultPropertyName = properties[i].Name;             int ichUnderscore = defaultPropertyName.IndexOf('_');             if (ichUnderscore <= 0)             {                 continue;             }             string navigationPropertyName = defaultPropertyName.Substring(0, ichUnderscore);             string targetKey = defaultPropertyName.Substring(ichUnderscore + 1);              string newPropertyName;             if (targetKey.StartsWith(navigationPropertyName))             {                 newPropertyName = targetKey;             }             else             {                 newPropertyName = navigationPropertyName + targetKey;             }             properties[i].Name = newPropertyName;         }     }  } 

Note that the Convention is added to your DbContext in your DbContext.OnModelCreating override, using:

modelBuilder.Conventions.Add(new ForeignKeyNamingConvention()); 
like image 186
crimbo Avatar answered Sep 20 '22 18:09

crimbo


You can do one of two things:

  1. Follow EF conventions in naming of foreign keys, i.e. if you have virtual Address, define your key property as AddressId

  2. Tell EF explicitly what to use. One way to do this is with Fluent API, as you are currently doing. You can also use data annotations, though:

    [ForeignKey("Address")] public int? AddressGlobalKey { get; set; }  public virtual Address Address { get; set; } 

That's your only choices.

like image 23
Chris Pratt Avatar answered Sep 23 '22 18:09

Chris Pratt