我的项目中有多个课程(包括TPT)。每个POCO都有一个BaseClass
,其中GUID
(称为GlobalKey
)作为主键。
首先,我使用DataAnnotations
来创建正确的外键。但是我在将对应的GUID与对象本身同步时遇到了问题。
现在我想只有一个虚拟导航属性,以便NamingConvention
创建数据库中的GUID字段。但是字段名称总是添加一个下划线,后跟单词GlobalKey
(右边)。当我想删除下划线时,我不想通过流畅的API通过我的所有POCO来执行此操作:
// Remove underscore from Navigation-Field
modelBuilder.Entity<Person>()
.HasOptional(x => x.Address)
.WithMany()
.Map(a => a.MapKey("AddressGlobalKey"));
通过覆盖惯例为所有POCOS做任何想法吗?
提前致谢。
安德烈亚斯
答案 0 :(得分:66)
我终于通过编写自定义约定找到了答案。此约定适用于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;
}
}
}
请注意,您的DbContext
覆盖中的DbContext.OnModelCreating
会使用以下内容添加公约:
modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
答案 1 :(得分:5)
你可以做以下两件事之一:
在外键命名中遵循EF约定,即如果您有虚拟Address
,请将您的关键属性定义为AddressId
明确告诉EF使用什么。一种方法是使用Fluent API,就像您目前所做的那样。不过,您也可以使用数据注释:
[ForeignKey("Address")]
public int? AddressGlobalKey { get; set; }
public virtual Address Address { get; set; }
这是你唯一的选择。
答案 2 :(得分:5)
我知道这有点旧,但这里有一个示例我如何通过我的流畅配置(OnModelCreating)指定映射列:
modelBuilder.Entity<Application>()
.HasOptional(c => c.Account)
.WithMany()
.Map(c => c.MapKey("AccountId"));
希望这有帮助,
答案 3 :(得分:1)
当字段类型关闭时,我也看到了同样的问题。仔细检查字段Ex:
的类型public string StateId {get;set;}
指向一个域对象,其中int为State.Id类型。确保您的类型相同。
答案 4 :(得分: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;
}
答案 5 :(得分: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);
}
}
}
}
答案 6 :(得分:0)
这些答案中的大多数都与Independent Assocations(其中&#34; MyOtherTable&#34;导航属性已定义,但不是&#34; int MyOtherTableId&#34;)而不是外键关联(两者都有)有关定义)。
这很好,因为问题是关于IA(它使用MapKey),但我在搜索FKA相同问题的解决方案时遇到了这个问题。由于其他人可能出于同样的原因来到这里,我想我会分享我使用ForeignKeyDiscoveryConvention的解决方案。