2011-11-05 9 views
5

Supongamos que tengo las dos clases siguientes:evitar especificar la columna clave en el doble escenario relacionado

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

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

he creado dos clases de asignación:

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); 
    } 
} 

esto se traducirá en una mesa Item que tiene una columna UserId y una columna OwnerId. Cuando uso KeyColumn("OwnerId") en la asignación HasMany, funciona solo con la columna OwnerId, pero me gustaría evitar eso. ¿Hay alguna manera de decirle a NHibernate que use la columna creada por la asignación en ItemMap?

por eso que quiero evitar especificar explícitamente la columna:
automáticamente se genera el nombre de la columna OwnerId basado en el nombre de la propiedad y algunas reglas. Si cambio las reglas o el nombre de la propiedad, también debo recordar cambiar KeyColumn. Entonces, básicamente, no se trata de refactorizar guardar.

+0

¿Se puede cambiar el nombre de la propiedad a Usuario? usuario virtual de usuario público {get; conjunto; }. Tendría que cambiar su asignación de referencias también, por supuesto, –

+0

@ColeW: Bueno, cambiar la propiedad a Usuario obviamente arreglaría eso, pero el nombre de la propiedad no sería tan bueno como lo es ahora. –

+0

Estoy de acuerdo, pero no creo que haya una forma clara de hacer lo que está pidiendo de otra manera. –

Respuesta

2

Actualizado: error corregido

si se aplican las reglas en los mapas a continuación

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); 
} 

si se aplican las reglas como las convenciones entonces usted necesita para poner en práctica IHasManyConvention y aplicar las mismas reglas en el EntityType y propertyname (que usted tiene que conseguir mediante la reflexión de la ChildType)

actualización:

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

Gracias, eso se ve bien. Sin embargo, me pregunto si está seguro de que este código funciona. Parece una recursión sin fin para mí: en el ctor de 'UserMap', creas una nueva instancia de' UserMap' ... ¿No sería mejor simplemente pasar 'this'? –

+0

es un error thx, corregido. en realidad esta fue una instantánea;) – Firo

+0

Gracias por el código - Acabo de probarlo. Lamentablemente, queda un problema: no se tienen en cuenta las convenciones para formatear las claves externas ... ¿Puede actualizar su código en consecuencia? Ejemplo: Mi convención es agregar "Id" al nombre de la propiedad. Su código agrega "_id". –

Cuestiones relacionadas