Как удалить подчеркивание полей внешнего ключа в коде сначала по соглашению
В моем проекте есть несколько классов (включая TPT). Каждый POCO имеет BaseClass
, который имеет GUID
(называемый GlobalKey
) в качестве первичного ключа.
Сначала я использовал DataAnnotations
для создания правильных внешних ключей. Но тогда у меня проблемы с синхронизацией соответствующего GUID с самим объектом.
Теперь я хочу иметь только одно свойство виртуальной навигации, чтобы поле GUID в базе данных создавалось NamingConvention
. Но имя поля всегда добавляет символ подчеркивания, за которым следует слово GlobalKey
(что правильно). Когда я хочу удалить подчеркивание, я не хочу проходить через все мои POCO в свободном API для этого:
// Remove underscore from Navigation-Field
modelBuilder.Entity<Person>()
.HasOptional(x => x.Address)
.WithMany()
.Map(a => a.MapKey("AddressGlobalKey"));
Любые идеи сделать это для всех POCOS, перезаписав соглашение?
Спасибо заранее.
Андреас
Ответы
Ответ 1
Наконец-то я нашел ответ на это, написав пользовательское соглашение. Это соглашение работает в 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;
}
}
}
Обратите внимание, что Конвенция добавляется к вашему DbContext
в вашем переопределении DbContext.OnModelCreating
, используя:
modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
Ответ 2
Вы можете сделать одну из двух вещей:
-
Следуйте соглашениям EF при именовании внешних ключей, т.е. если у вас есть виртуальный Address
, укажите свое свойство ключа как AddressId
-
Скажите EF явно, что использовать. Один из способов сделать это - с Fluent API, как вы сейчас это делаете. Вы также можете использовать аннотации данных:
[ForeignKey("Address")]
public int? AddressGlobalKey { get; set; }
public virtual Address Address { get; set; }
Это ваш единственный выбор.
Ответ 3
Я знаю, что это немного устарело, но вот пример того, как я определяю столбцы сопоставления через свою текущую конфигурацию (OnModelCreating):
modelBuilder.Entity<Application>()
.HasOptional(c => c.Account)
.WithMany()
.Map(c => c.MapKey("AccountId"));
Надеюсь, что это поможет,
Ответ 4
Я также видел ту же проблему, когда тип поля выключен. Дважды проверьте тип поля Ex:
public string StateId {get;set;}
указывающий на объект домена с int как тип State.Id. Убедитесь, что ваши типы одинаковы.
Ответ 5
Я обнаружил, что настройки ключевого столбца не были захвачены организацией 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;
}
Ответ 6
У меня возникли проблемы при объединении его с соглашением об именах имен 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);
}
}
}
}
Ответ 7
Большинство этих ответов связаны с независимыми ассоциациями (где определено свойство навигации "MyOtherTable", но не "int MyOtherTableId" ), а не внешние ассоциации ключей (где оба определены).
Это прекрасно, так как вопрос об IA (он использует MapKey), но я столкнулся с этим вопросом при поиске решения той же проблемы с FKAs. Поскольку другие люди могут приехать сюда по той же причине, я решил поделиться своим решением, которое использует ForeignKeyDiscoveryConvention.
fooobar.com/info/124705/...