0
votes

I use NHibernates schema export functionality to create and update the database.

However sometimes (seems to happen on slow machines more frequently) NHibernate generates incorrect alter table scripts. I disabled the schema export and log the statements to debug the problem.

This problem occurs randomly and affects randomly different tables.

An example script is: alter table dbo.VlogNet_NumberGroups add DKS_P03_Artikelgruppe BIGINT

However the "DKS_P03_Artikelgruppe" column belongs to a completely different mapping and to make it worse this mapping is even configured to NHibernate.Mapping.ByCode.SchemaAction.None.

Edit: To clarify my question: Why does NHibernate randomly mix up different mappings and issues alter table commands which break my database.

Mappings:

public class NumberGroupMap : HierachicalEntityMap<NumberGroup>
{
    public NumberGroupMap()
    {
        Property(x => x.StartNumber);
        Property(x => x.EndNumber);
        Property(x => x.LastGeneratedNumber);
        Property(x => x.StartTime);
        Property(x => x.LifeSpan);
        Property(x => x.ResetOnNewYear);
        Property(x => x.IsResetable);
        Property(x => x.WarningAtPos);
    }

    public override string TableName
    {
        get { return "VlogNet_NumberGroups"; }
    }
}

HierachicalEntityMap<T> derives from ClassMapping<T>, it just adds some Properties (ID, Name, Parent) and sets the TableName to the value of the corresponding Property.

The field DKS_P03_Artikelgruppe originates from this mapping:

public class DksCarrierInformationMap : ClassMapping<DksCarrierInformation>
{
    public const string EntityTable = "DKS_Liste";

    public DksCarrierInformationMap()
    {
        Table(EntityTable);
        SchemaAction(NHibernate.Mapping.ByCode.SchemaAction.None);

        Id(x => x.Auto_ID, x => x.Generator(Generators.Identity));
        Property(x => x.Versanddaten_ID, x => { x.NotNullable(true); x.Unique(true); });

        #region Invoiceaddress
        // ... left for brevity
        #endregion

        #region Labelinformationen

        // ... left for brevity

        #endregion

        #region EDI

        // ... left for brevity

        Component(x => x.DKS_P01, x =>
                                      {
                                          x.Property(c => c.Artikelgruppe, c => c.Column("DKS_P01_Artikelgruppe"));
                                          x.Property(c => c.Anz_Teile, c => c.Column("DKS_P01_Anz_Teile"));
                                          x.Property(c => c.Anz_Buegel, c => c.Column("DKS_P01_Anz_Buegel"));
                                          x.Property(c => c.Gewicht, c => c.Column("DKS_P01_Gewicht"));
                                          x.Property(c => c.Type, c => { c.Column("DKS_P01_ArtikelTyp"); c.Type>(); c.Length(10); });
                                          x.Property(c => c.Bezeichnung, c => c.Column("DKS_P01_Bezeichnung"));
                                      });

        // ... left for brevity

        Component(x => x.DKS_P03, x =>
                                      {
                                          x.Property(c => c.Artikelgruppe, c => c.Column("DKS_P03_Artikelgruppe"));
                                          x.Property(c => c.Anz_Teile, c => c.Column("DKS_P03_Anz_Teile"));
                                          x.Property(c => c.Anz_Buegel, c => c.Column("DKS_P03_Anz_Buegel"));
                                          x.Property(c => c.Gewicht, c => c.Column("DKS_P03_Gewicht"));
                                          x.Property(c => c.Type, c => { c.Column("DKS_P03_ArtikelTyp"); c.Type>(); c.Length(10); });
                                          x.Property(c => c.Bezeichnung, c => c.Column("DKS_P03_Bezeichnung"));
                                      });

        // ... left for brevity

        Component(x => x.DKS_P20, x =>
                                      {
                                          x.Property(c => c.Artikelgruppe, c => c.Column("DKS_P20_Artikelgruppe"));
                                          x.Property(c => c.Anz_Teile, c => c.Column("DKS_P20_Anz_Teile"));
                                          x.Property(c => c.Anz_Buegel, c => c.Column("DKS_P20_Anz_Buegel"));
                                          x.Property(c => c.Gewicht, c => c.Column("DKS_P20_Gewicht"));
                                          x.Property(c => c.Type, c => { c.Column("DKS_P20_ArtikelTyp"); c.Type>(); c.Length(10); });
                                          x.Property(c => c.Bezeichnung, c => c.Column("DKS_P20_Bezeichnung"));
                                      });

        #endregion
    }
}

The code which sets up the NHibernate SessionFactory:

var modelMapper = new ModelMapper();
foreach (var assembly in _assembliesToScan)
{
    Logger.DebugFormat(" * {0}", assembly);
    modelMapper.AddMappings(assembly.GetTypes());
}

Logger.InfoFormat("Adding {0} class maps", _classMaps.Count);
foreach (var type in _classMaps)
{
    Logger.DebugFormat(" * {0}", type);
    modelMapper.AddMapping(type);
}

var cfg = new Configuration();

ConfigureNHibernate(cfg, Settings[Type]);
ConfigureEnvironment(cfg);
cfg.AddMapping(modelMapper.CompileMappingForAllExplicitlyAddedEntities());

ValidateSchema(cfg);

SessionFactory = cfg.BuildSessionFactory();

1
I want to understand why NHibernate mixes up different mappings and issues alter table commands which break my database.Zebi
I dont see ANY SQL ALTER commands?D3vtr0n
could you show where the schemaexport is actually performed and where the mappings are added to the sessionfactory? If the mappings themselves work alright and the schemaexport works most of the time i'd suspect some timing-critical access problem with the NH session factory, maybe due to multithreading. In what kind of environment (setup project, winforms app, WCF service, ASP.Net website etc.) is the schemaexport executed?Dirk Trilsbeek
i assume the sessionfactory is a threadsafe singleton, so that multiple inits shouldn't occur (although it's questionable if they even resulted in random alter table-statements). One thing you could check is if you have redundant mappings somewhere in your assemblies in the _assembliesToScan collection. But that's just guesswork, as those mappings would probably also mess up NH mapping in the running application. The only weird detail i just noticed is the possible mixture of mapping by code (ClassMapping) and FluentNHibernate in your mappings (ClassMap<T>). No idea how that would behave.Dirk Trilsbeek
this is weird enough to actually get started debugging NH itself. I recommend you download the NH sources for the version you currently use and add some logging specific to your problem. The actual script generation takes place in NHibernate.Cfg.Configuration.GenerateSchemaUpdateScript(). An example for metadata fetching is in NHibernate.Tool.hbm2ddl.SchemaUpdate.Execute(). You could build a small test application that only creates the mappings, fetches the metadata from the database and creates the script.Dirk Trilsbeek

1 Answers

1
votes

Sounds like it could be related to this?

NHibernate - Wrong Columns on Queries

There's since been an update to the NHibernate source code (v 3.3.4), which should have fixed the problem.

See for more details: https://groups.google.com/forum/#!topic/nhusers/BZoBoyWQEvs