2011-05-23 13 views
6

Esto es muy similar a mi anterior pregunta: FluentNHibernate: How to translate HasMany(x => x.Addresses).KeyColumn("PersonId") into automappingFluentNHibernate: relación Automapping OneToMany utilizando atributos y convenciones


decir que tengo estos modelos:

public class Person 
{ 
    public virtual int Id { get; private set; } 
    public virtual ICollection<Address> Addresses { get; private set; } 
} 

public class Address 
{ 
    public virtual int Id { get; private set; } 
    public virtual Person Owner { get; set; } 
} 

Quiero FluentNHibernate para crear las siguientes tablas :

Person 
    PersonId 
Address 
    AddressId 
    OwnerId 

Esto se puede lograr fácilmente mediante el uso de fluen t mapeo:

public class PersonMapping : ClassMap<Person> 
{ 
    public PersonMapping() 
    { 
     Id(x => x.Id).Column("PersonId"); 
     HasMany(x => x.Addresses).KeyColumn("OwnerId"); 
    } 
} 

public class AddressMapping : ClassMap<Address> 
{ 
    public AddressMapping() 
    { 
     Id(x => x.Id).Column("AddressId"); 
     References(x => x.Person).Column("OwnerId"); 
    } 
} 

Quiero obtener el mismo resultado utilizando el mapeo automático. Probé las siguientes convenciones:

class PrimaryKeyNameConvention : IIdConvention 
{ 
    public void Apply(IIdentityInstance instance) 
    { 
     instance.Column(instance.EntityType.Name + "Id"); 
    } 
} 

class ReferenceNameConvention : IReferenceConvention 
{ 
    public void Apply(IManyToOneInstance instance) 
    { 
     instance.Column(string.Format("{0}Id", instance.Name)); 
    } 
} 

// Copied from @Fourth: https://stackoverflow.com/questions/6091290/fluentnhibernate-how-to-translate-hasmanyx-x-addresses-keycolumnpersonid/6091307#6091307 
public class SimpleForeignKeyConvention : ForeignKeyConvention 
{ 
    protected override string GetKeyName(Member property, Type type) 
    { 
     if(property == null) 
      return type.Name + "Id"; 
     return property.Name + "Id"; 
    } 
} 

pero creó las siguientes tablas:

Person 
    PersonId 
Address 
    AddressId 
    OwnerId 
    PersonId // this column should not exist 

por lo que añade un AutoMappingOverride:

public class PersonMappingOverride : IAutoMappingOverride<Person> 
{ 
    public void Override(AutoMapping<Person> mapping) 
    { 
     mapping.HasMany(x => x.Addresses).KeyColumn("OwnerId"); 
    } 
} 

Esto resolvió correctamente el problema. Pero quiero obtener el mismo resultado utilizando el atributo & convención. Probé:

public class Person 
{ 
    public virtual int Id { get; private set; } 

    [KeyColumn("OwnerId")] 
    public virtual ICollection<Address> Addresses { get; private set; } 
} 

class KeyColumnAttribute : Attribute 
{ 
    public readonly string Name; 

    public KeyColumnAttribute(string name) 
    { 
     Name = name; 
    } 
} 

class KeyColumnConvention: IHasManyConvention 
{ 
    public void Apply(IOneToManyCollectionInstance instance) 
    { 
     var keyColumnAttribute = (KeyColumnAttribute)Attribute.GetCustomAttribute(instance.Member, typeof(KeyColumnAttribute)); 
     if (keyColumnAttribute != null) 
     { 
      instance.Key.Column(keyColumnAttribute.Name); 
     } 
    } 
} 

pero creó estas tablas:

Person 
    PersonId 
Address 
    AddressId 
    OwnerId 
    PersonId // this column should not exist 

A continuación se muestra el resto de mi código:

ISessionFactory sessionFactory = Fluently.Configure() 
    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)) 
    .Mappings(m => 
       m.AutoMappings.Add(AutoMap.Assemblies(typeof(Person).Assembly) 
        .Conventions.Add(typeof(PrimaryKeyNameConvention)) 
          .Conventions.Add(typeof(PrimaryKeyNameConvention)) 
          .Conventions.Add(typeof(ReferenceNameConvention)) 
          .Conventions.Add(typeof(SimpleForeignKeyConvention)) 
          .Conventions.Add(typeof(KeyColumnConvention))) 

       //m.FluentMappings 
       // .Add(typeof (PersonMapping)) 
       // .Add(typeof (AddressMapping)) 
    ) 
    .ExposeConfiguration(BuildSchema) 
    .BuildConfiguration() 
    .BuildSessionFactory(); 

¿Alguna idea? Gracias.


Actualización:

El proyecto de prueba se puede descargar desde here.

Respuesta

6

Sigh ... Aprender NHibernate es realmente una experiencia de extracción de cabello.

De todos modos creo que finalmente descubrí cómo resolver este problema: simplemente elimine el SimpleForeignKeyConvention y todo va a funcionar bien.

Parece que el SimpleForeignKeyConvention entra en conflicto con ambos ReferenceKeyConvention & KeyColumnConvention. Tiene una prioridad más alta que KeyColumnConvention pero una prioridad más baja que ReferenceKeyConvention.

public class SimpleForeignKeyConvention : ForeignKeyConvention 
{ 
    protected override string GetKeyName(Member property, Type type) 
    { 
     if(property == null) 
      // This line will disable `KeyColumnConvention` 
      return type.Name + "Id"; 

     // This line has no effect when `ReferenceKeyConvention` is enabled. 
     return property.Name + "Id"; 
    } 
} 
+0

se preocupan por aceptar la respuesta, por lo que otros saben que es la solución a su problema – Firo

+0

@Firo: recibí un mensaje _ "Puede aceptar su propia respuesta en x horas" _ (por el momento, x = 16). Volveré a intentarlo mañana. –

1

He probado sus clases con la función de asignación automática de FHN y no crea esa segunda tabla PersonId en la dirección. Estoy usando FHN v1.2.0.721 de here

+0

Gracias por probar! ¿Incluyó 'PersonMappingOverride' en la configuración? Si es así, sí, no crea esa segunda 'PersonId' en la tabla' Dirección'.Pero se supone que está excluido de la configuración, porque quiero obtener el mismo resultado usando 'KeyColumnAttribute' y' KeyColumnConvention'. He actualizado mi pregunta con un enlace de descarga de mi proyecto de prueba, que debe reproducir el problema. –

+1

Ahora ha cambiado los nombres de las propiedades, ¿verdad? (para probar que ...) Entonces, lea este hilo similar (Para evitar tener dos columnas de clave externa diferentes ...): http://stackoverflow.com/questions/310641/fluent-nhibernate-how-to-create-one -to-many-bidirectional-mapping – VahidN

Cuestiones relacionadas