45

我的项目中有多个类(包括TPT)。每个POCO都有一个BaseClass,其中有一个GUID(称为GlobalKey)作为主键。如何在代码中首先删除下划线中的外键字段

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

现在我想只有一个虚拟导航属性,以便数据库中的GUID字段由NamingConvention创建。但字段名称总是添加一个下划线,后面跟着GlobalKey(这是正确的)。当我想删除下划线,我不想去通过我的所有波苏斯流利的API来做到这一点的:

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

任何想法要做到这一点对所有POCOS由于覆盖惯例?

在此先感谢。

安德烈亚斯

+0

你不能这样做 - 但。有一个[可定制的代码优先开发功能的约定](http://msdn.microsoft.com/en-us/data/jj819164.aspx)正在为EF 6.0工作,但现在 - 你必须处理这个你自己,手动... –

+0

我实际上使用EF6 alpha 2.是否有可能以这种方式使用它? –

+0

[查看此博客文章](http://blog.3d-logic.com/2013/03/24/my-first-encounter-with-custom-conventions-in-entity-framework-6/) - this同事似乎在做一些类似于你想要的事情。 –

回答

63

我终于找到了一个答案,通过编写一个自定义约定。该惯例适用于EF 6.0 RC1(上周的代码),所以我认为在EF 6.0发布后它可能会继续工作。

使用此方法,标准EF约定标识独立关联(IAs),然后为外键字段创建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()); 
+7

而不是最后添加约定,您可以在ForeignKeyIndexConvention之前添加它,以将命名约定应用于外键索引名称。 'modelBuilder.Conventions.AddBefore (new ForeignKeyNamingConvention( ));' –

+0

这段代码对我来说不太合适,因为这个测试总是为false:if(!properties [i] .Name.EndsWith(“_”+ otherEndProperties [i] .Name))事实上,Name本身已经包含下划线。所以我跳过了整个测试,总是调用NormalizeForeignKeyProperties。 –

+0

这是故意的 - 如果属性Name有下划线,则DoPropertiesHaveDefaultNames()应该返回true。这听起来像你正在发生的事情? 检查的原因是,它不会覆盖其他约定修复的外键名称,也不会覆盖配置时指定的外键名称。 – crimbo

5

你可以做两件事情之一:

  1. 的外键,即命名按照EF约定,如果你有虚拟Address,定义关键属性为AddressId

  2. 明确告诉EF要使用什么。一种方法是使用Fluent API,就像你现在正在做的那样。你也可以用数据说明,虽然:

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

那是你唯一的选择。

+0

我以前做过这件事,但我遇到了同步问题。所以我正在寻找一种在EF6中做到这一点的方法。 –

+0

你得到了什么错误?这是非常基本的功能,应该仍然可以在EF6中使用(除非某些东西坏了,这对于预发行软件来说当然是可能的)。 –

+0

当试图调用SaveChanges()时,我得到了PK约束错误。即,在这种情况下,当我尝试保存包含对地址对象的引用的人物对象之前创建和保存的地址对象时。导航属性是正确的,而相应的Guid-Property不是(null或Guid.Empty),这通常没有问题。 (继续...) –

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; 
} 
+0

我认为如果在属性集合中有多个列,则您已经引入了一个错误,因为您在第一次成功时返回true –

0

我在将它与id命名约定的EntityNameId组合时遇到了问题。

使用以下约定确保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); 
       }     
      } 
     } 
    } 
5

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

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

希望这有助于

+1

为什么这是低调的? – ProfK

+0

@ProfK我没有投票,但我猜测是因为OP想要一个将被全面应用的约定。 – Casey

1

我有当字段类型关闭时也会出现同样的问题。请仔细检查字段的类型:例如:

public string StateId {get;set;} 

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

0

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

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

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

相关问题