Search code examples
c#nhibernatenhibernate-mapping

NHibernate Exception while building SessionFactory if column name is same for association property


I have Master and Detail tables. Name of Primary Key column in both the tables is same: Id.
While building session factory, I am getting following exception:

NHibernate.MappingException: Unable to build the insert statement for class ........Detail1Entity: a failure occured when adding the Id of the class ---> System.ArgumentException: The column 'Id' has already been added in this SQL builder
Parameter name: columnName
   at NHibernate.SqlCommand.SqlInsertBuilder.AddColumnWithValueOrType(String columnName, Object valueOrType)
   at NHibernate.SqlCommand.SqlInsertBuilder.AddColumns(String[] columnNames, Boolean[] insertable, IType propertyType)
   at NHibernate.Persister.Entity.AbstractEntityPersister.GenerateInsertString(Boolean identityInsert, Boolean[] includeProperty, Int32 j)
   --- End of inner exception stack trace ---
   at NHibernate.Persister.Entity.AbstractEntityPersister.GenerateInsertString(Boolean identityInsert, Boolean[] includeProperty, Int32 j)
   at NHibernate.Persister.Entity.AbstractEntityPersister.GenerateInsertString(Boolean[] includeProperty, Int32 j)
   at NHibernate.Persister.Entity.AbstractEntityPersister.PostInstantiate()
   at NHibernate.Persister.Entity.SingleTableEntityPersister.PostInstantiate()
   at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
   at NHibernate.Cfg.Configuration.BuildSessionFactory()
   at ...........

I have following tables:

CREATE Table MasterTable1
(
[Id] [INT] IDENTITY (1,1) NOT NULL CONSTRAINT MasterTable1PK PRIMARY KEY,
[MasterData] [VARCHAR] (100)
)

CREATE Table DetailTable1
(
[Id] [INT] IDENTITY (1,1) NOT NULL CONSTRAINT DetailTable1PK PRIMARY KEY,
[MasterId] [INT] NOT NULL CONSTRAINT DetailTable1_MasterTable1_FK FOREIGN KEY REFERENCES MasterTable(MasterId),
[DetailData] [VARCHAR] (100)
)

Following are entity definitions:

public class Master1Entity
{
    public virtual int Id { get; set; }
    public virtual string MasterData { get; set; }
}

public class Detail1Entity
{
    public virtual int Id { get; set; }
    public virtual string DetailData { get; set; }

    public virtual Master1Entity Master1 { get; set; }
}

Following are the mappings:

internal class Master1Map : ClassMapping<Master1Entity>
{
    public Master1Map()
    {
        Table("MasterTable1");

        Id(x => x.Id, im =>
        {
            im.Column("Id");
            im.Generator(Generators.Identity);
        });
        Property(x => x.MasterData);
    }
}

internal class Detail1Map : ClassMapping<Detail1Entity>
{
    public Detail1Map()
    {
        Table("DetailTable1");

        Id(x => x.Id, im =>
        {
            im.Column("Id");
            im.Generator(Generators.Identity);
        });
        Property(x => x.DetailData);

        ManyToOne(x => x.Master1, map => { map.Column("Id"); });
    }
}

As explained here, I know that if I rename [MasterTable1].[Id] to [MasterTable1].[MasterId] and modify the entities and mappings accordingly, this will work well.

But, I cannot change the underlying database; this is legacy database I have to take on.

Also, this is Many-to-One relationship. One Master will have multiple Detail entries.

Is there any way to make this work without changing column name in database?


Solution

  • Don't confuse collection mapping (expects column from association table) with entity mapping (expects column from owner table). ManyToOne is entity mapping and it expects column from owner table. In your case owner table for Master1 is DetailTable1 and column you need to map is MasterId:

    internal class Detail1Map : ClassMapping<Detail1Entity>
    {
    ...
     ManyToOne(x => x.Master1, map => { map.Column("MasterId"); });