J'ai plusieurs classes (y compris TPT) dans mon projet. Chaque POCO a une BaseClass
, qui a une GUID
(appelée GlobalKey
) comme clé primaire.
J'ai d'abord utilisé DataAnnotations
pour créer les clés étrangères correctes. Mais ensuite, j'ai des problèmes pour synchroniser le GUID correspondant avec l'objet lui-même.
Maintenant, je souhaite avoir une seule propriété de navigation virtuelle pour que le champ GUID de la base de données soit créé par NamingConvention
. Mais le nom du champ ajoute toujours un trait de soulignement suivi du mot GlobalKey
(ce qui est correct). Lorsque je veux supprimer le trait de soulignement, je ne veux pas passer par tous mes POCO dans l'API fluide pour faire ceci:
// Remove underscore from Navigation-Field
modelBuilder.Entity<Person>()
.HasOptional(x => x.Address)
.WithMany()
.Map(a => a.MapKey("AddressGlobalKey"));
Des idées pour faire cela pour tous les POCOS en écrasant une convention?
Merci d'avance.
Andreas
J'ai finalement trouvé une réponse à cela, en écrivant une convention personnalisée. Cette convention fonctionne dans EF 6.0 RC1 (code de la semaine dernière), donc je pense que cela va probablement continuer à fonctionner après la publication de EF 6.0.
Avec cette approche, les conventions EF standard identifient les associations indépendantes (IA), puis créent le champ EdmProperty pour le champ de clé étrangère. Ensuite, cette convention arrive et renomme les champs de clé étrangère.
/// <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;
}
}
}
Notez que la Convention est ajoutée à votre DbContext
dans votre substitution DbContext.OnModelCreating
en utilisant:
modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
Vous pouvez faire l'une des deux choses suivantes:
Suivez les conventions EF pour nommer les clés étrangères, c’est-à-dire que si vous avez virtuel Address
, définissez votre propriété de clé comme suit: AddressId
Dites explicitement à EF ce qu'il faut utiliser. Une façon de faire est d'utiliser l'API Fluent, comme vous le faites actuellement. Vous pouvez également utiliser des annotations de données, cependant:
[ForeignKey("Address")]
public int? AddressGlobalKey { get; set; }
public virtual Address Address { get; set; }
C'est ton seul choix.
Je sais que cela est un peu vieux, mais voici un exemple de la façon dont je spécifie des colonnes de mappage dans ma configuration courante (OnModelCreating):
modelBuilder.Entity<Application>()
.HasOptional(c => c.Account)
.WithMany()
.Map(c => c.MapKey("AccountId"));
J'espère que cela t'aides,
J'ai également vu le même problème lorsque le type de champ est désactivé. Vérifiez le type du champ Ex:
public string StateId {get;set;}
pointant vers un objet de domaine avec int comme type State.Id. Assurez-vous que vos types sont les mêmes.
La plupart de ces réponses concernent les associations indépendantes (où la propriété de navigation "MyOtherTable" est définie, mais pas "int MyOtherTableId") au lieu d'associations de clé étrangère (où les deux sont définies).
Cela convient puisque la question concerne l’IA (elle utilise MapKey), mais j’ai rencontré cette question lorsqu’on a cherché une solution au même problème avec les FKA. Etant donné que d'autres personnes peuvent venir ici pour la même raison, j'ai pensé partager ma solution utilisant une convention ForeignKeyDiscoveryConvention.
J'ai constaté que la personnalisation des colonnes de clé n'était pas interceptée par la Convention ForeignKeyNaming. Fait ce changement pour les attraper.
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;
}
J'ai eu des problèmes lors de la combinaison avec une convention de nommage id de EntityNameId.
Lorsque vous utilisez la convention suivante pour vous assurer que la table Customer a CustomerId plutôt que simplement Id.
modelBuilder.Properties()
.Where(p => p.Name == "Id")
.Configure(p => p.IsKey().HasColumnName(p.ClrPropertyInfo.ReflectedType == null ? "Id" : p.ClrPropertyInfo.ReflectedType.Name +"Id"));
La convention de dénomination de la clé étrangère doit être modifiée comme suit.
/// <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);
}
}
}
}