51

我的项目中有多个课程(包括 TPT)。每个 POCO 都有一个BaseClass,它有一个GUID(称为GlobalKey)作为主键。

首先,我用来DataAnnotations创建正确的外键。但是我在将相应的 GUID 与对象本身同步时遇到了问题。

现在我只想拥有一个虚拟导航属性,以便数据库中的 GUID 字段由NamingConvention. 但是字段名称总是加一个下划线,后跟单词GlobalKey(这是正确的)。当我想删除下划线时,我不想通过 fluent API 中的所有 POCO 来执行此操作:

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

通过覆盖约定为所有 POCOS 执行此操作的任何想法?

提前致谢。

安德烈亚斯

4

7 回答 7

67

通过编写自定义约定,我终于找到了答案。这个约定在 EF 6.0 RC1(上周的代码)中有效,所以我认为在 EF 6.0 发布后它可能会继续有效。

使用这种方法,标准 EF 约定识别独立关联 (IA),然后为外键字段创建 EdmProperty。然后这个约定出现并重命名外键字段。

/// <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;
        }
    }

}

请注意,使用以下命令将约定添加到您DbContextDbContext.OnModelCreating覆盖中:

modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
于 2013-08-15T01:59:32.263 回答
6

您可以执行以下两项操作之一:

  1. 在外键命名时遵循 EF 约定,即如果您有 virtual Address,请将您的 key 属性定义为AddressId

  2. 明确告诉 EF 使用什么。一种方法是使用 Fluent API,就像您目前正在做的那样。不过,您也可以使用数据注释:

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

那是你唯一的选择。

于 2013-03-28T17:15:10.127 回答
5

我知道这有点老了,但这是我如何通过我的流利配置(OnModelCreating)指定映射列的示例:

modelBuilder.Entity<Application>()
            .HasOptional(c => c.Account)
                .WithMany()
                .Map(c => c.MapKey("AccountId"));

希望这可以帮助,

于 2015-03-10T13:29:58.783 回答
1

当字段的类型关闭时,我也看到了同样的问题。仔细检查字段 Ex 的类型:

public string StateId {get;set;}

指向以 int 作为 State.Id 类型的域对象。确保您的类型相同。

于 2015-05-05T03:58:53.510 回答
0

我发现键列自定义没有被 ForeignKeyNamingConvention 捕获。做这个改变是为了抓住他们。

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;
}
于 2014-08-13T15:58:41.577 回答
0

将它与 EntityNameId 的 id 命名约定结合使用时出现问题。

当使用以下约定来确保 Customer 表具有 CustomerId 而不是简单的 Id。

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

外键命名约定需要更改为以下内容。

 /// <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); 
                }                 
            } 
        } 
    }
于 2015-01-23T10:53:03.400 回答
0

这些答案中的大多数都与独立关联(定义了“MyOtherTable”导航属性,但没有定义“int MyOtherTableId”)而不是外键关联(定义了两者)有关。

这很好,因为问题是关于 IA(它使用 MapKey),但我在寻找 FKA 相同问题的解决方案时遇到了这个问题。由于其他人可能出于同样的原因来到这里,我想我会分享我使用 ForeignKeyDiscoveryConvention 的解决方案。

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

于 2017-05-05T16:02:46.210 回答