2011-11-05 9 views
5

Si supponga Ho le seguenti due classi:Evitare specificando colonna chiave a doppia scenario legato

public class User : Entity 
{ 
    public virtual IList<Item> Items { get; set; } 
} 

public class Item : Entity 
{ 
    public virtual User Owner { get; set; } 
} 

ho creato due classi di mappatura:

public class UserMap : ClassMap<User> 
{ 
    public UserMap() 
    { 
     Id(x => x.Id); 
     HasMany(x => x.Items); 
    } 
} 

public class ItemMap : ClassMap<Item> 
{ 
    public ItemMap() 
    { 
     Id(x => x.Id); 
     References(x => x.Owner); 
    } 
} 

questo si tradurrà in una tabella Item che ha una colonna UserId e una colonna OwnerId. Quando utilizzo KeyColumn("OwnerId") nella mappatura HasMany, funziona solo con la colonna OwnerId, ma vorrei evitarlo. C'è un modo per dire a NHibernate, di usare la colonna creata dal mapping in ItemMap?

perché voglio evitare di specificare la colonna in modo esplicito:
Il nome della colonna OwnerId viene automaticamente generato in base al nome della proprietà e alcune regole. Se cambio le regole o il nome della proprietà, devo ricordarmi di cambiare anche quello KeyColumn. Quindi, in pratica, non si tratta di rifattorizzare il salvataggio.

+0

È possibile modificare il nome della proprietà in Utente? utente virtuale utente pubblico {get; impostato; }. Dovresti cambiare anche la mappatura dei riferimenti. –

+0

@ColeW: Beh, cambiare la proprietà in Utente ovviamente la risolverebbe, ma il nome della proprietà non sarebbe buono come ora. –

+0

Sono d'accordo ma non credo che ci sia un modo pulito di fare ciò che stai chiedendo altrimenti. –

risposta

2

Aggiornato: bug

se si applica le regole nelle mappe poi

public static void KeyColumnFromReference<TChild>(
    this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map, Expression<Func<TChild, object>> referenceprop) 
{ 
    string propertyname = GetPropertyName(referenceprop); 
    var column = ((IMappingProvider)map).GetClassMapping() 
     .References.First(m => m.Name == propertyname) 
     .Columns.First().Name; 

    collectionmap.KeyColumn(column); 
} 

public static void KeyColumnFromReference<T, TChild>(
    this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map) 
{ 
    var column = ((IMappingProvider)map).GetClassMapping() 
     .References.First(m => m.Type == typeof(TChild)) 
     .Columns.First().Name; 

    collectionmap.KeyColumn(column); 
} 

public UserMap() 
{ 
    HasMany(x => x.Items) 
     .KeyColumnFromReference<User, Item>(new ItemMap()); 

    // or 

    HasMany(x => x.Items) 
     .KeyColumnFromReference(new ItemMap(), u => u.Owner); 
} 

se si applica le regole delle convenzioni allora avete bisogno di implementare IHasManyConvention e applicare le stesse regole sulla EntityType e propertyname (che si deve ottenere attraverso la riflessione dal ChildType)

Aggiornamento:

class ForeignKeyConvention : IHasManyConvention 
{ 
    public void Apply(IOneToManyCollectionInstance instance) 
    { 
     // to force the compiler to take the Name property and not the Name method 
     string propertyName = ((ICollectionInspector)instance).Name; 

     // should be equal to the convention for the reference key column 
     instance.Key.Column(propertyName + instance.EntityType.Name + "id"); 
    } 
} 
+0

Grazie, sembra buono. Tuttavia, mi chiedo se sei sicuro che questo codice funzioni. Mi sembra una ricorsione senza fine: nel ctor di 'UserMap', crei una nuova istanza di' UserMap' ... Non sarebbe meglio passare semplicemente 'this'? –

+0

è un bug thx, risolto. in realtà questo è stato uno scatto rapido;) – Firo

+0

Grazie per il codice - l'ho appena testato. Purtroppo, c'è rimasto un problema: non tiene conto delle convenzioni per la formattazione delle chiavi esterne ... Puoi aggiornare il tuo codice di conseguenza? Esempio: la mia convenzione è di aggiungere "Id" al nome della proprietà. Il tuo codice aggiunge "_id". –