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

前端 未结 7 909
傲寒
傲寒 2020-11-30 01:20

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

相关标签:
7条回答
  • 2020-11-30 01:26

    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());
    
    0 讨论(0)
  • 2020-11-30 01:30

    I found that key column customizations were not being caught by the ForeignKeyNamingConvention. Made this change to catch them.

    private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties)
    {
        if (properties.Count == otherEndProperties.Count)
        {
            for (int i = 0; i < properties.Count; ++i)
            {
                if (properties[i].Name.EndsWith("_" + otherEndProperties[i].Name))
                {
                    return true;
                }
                else
                {
                    var preferredNameProperty =
                        otherEndProperties[i]
                            .MetadataProperties
                            .SingleOrDefault(x => x.Name.Equals("PreferredName"));
    
                    if (null != preferredNameProperty)
                    {
                        if (properties[i].Name.EndsWith("_" + preferredNameProperty.Value))
                        {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    0 讨论(0)
  • 2020-11-30 01:40

    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.

    0 讨论(0)
  • 2020-11-30 01:43

    I had issues when combining it with an id naming convention of EntityNameId.

    When using the following convention to ensure the Customer table has CustomerId rather than simply Id.

    modelBuilder.Properties()
                            .Where(p => p.Name == "Id")
                            .Configure(p => p.IsKey().HasColumnName(p.ClrPropertyInfo.ReflectedType == null ? "Id" : p.ClrPropertyInfo.ReflectedType.Name +"Id"));
    

    The foreign key naming convention needs to be changed to the following.

     /// <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 ForeignKey properties (including IAs)  
                if (!association.IsForeignKey) return;
    
                // rename FK columns  
                var constraint = association.Constraint; 
                if (DoPropertiesHaveDefaultNames(constraint.FromProperties, constraint.ToProperties)) 
                { 
                    NormalizeForeignKeyProperties(constraint.FromProperties); 
                } 
    
                if (DoPropertiesHaveDefaultNames(constraint.ToProperties, constraint.FromProperties)) 
                { 
                    NormalizeForeignKeyProperties(constraint.ToProperties); 
                }
            } 
    
            private static bool DoPropertiesHaveDefaultNames(IReadOnlyList<EdmProperty> properties, IReadOnlyList<EdmProperty> otherEndProperties) 
            { 
                if (properties.Count != otherEndProperties.Count) 
                { 
                    return false; 
                } 
    
                for (var i = 0; i < properties.Count; ++i)
                {
                    if (properties[i].Name.Replace("_", "") != otherEndProperties[i].Name) 
                    { 
                        return false; 
                    } 
                } 
    
                return true; 
            } 
    
            private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties) 
            { 
                for (var i = 0; i < properties.Count; ++i) 
                { 
                    var underscoreIndex = properties[i].Name.IndexOf('_'); 
                    if (underscoreIndex > 0) 
                    { 
                        properties[i].Name = properties[i].Name.Remove(underscoreIndex, 1); 
                    }                 
                } 
            } 
        }
    
    0 讨论(0)
  • 2020-11-30 01:46

    Most of these answers have to do with Independent Assocations (where the "MyOtherTable" navigation property is defined, but not the "int MyOtherTableId") instead of Foreign Key Assocations (where both are defined).

    That is fine since the question is about IA (it uses MapKey), but I came across this question when searching for a solution to the same problem with FKAs. Since other people may come here for the same reason, I thought I would share my solution that uses a ForeignKeyDiscoveryConvention.

    https://stackoverflow.com/a/43809004/799936

    0 讨论(0)
  • 2020-11-30 01:48

    I have also seen the same problem when the type of the field is off. Double check the type of the field Ex:

    public string StateId {get;set;}
    

    pointing to a domain object with int as the State.Id type. Make sure that your types are same.

    0 讨论(0)
提交回复
热议问题