0

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

Zebi
  • 8,682
  • 1
  • 36
  • 42
  • I want to understand why NHibernate mixes up different mappings and issues alter table commands which break my database. – Zebi Nov 16 '12 at 15:49
  • I dont see ANY SQL ALTER commands? – D3vtr0n Nov 16 '12 at 16:10
  • see 4th paragraph, I edited my question to make it **bold** – Zebi Nov 16 '12 at 16:23
  • 1
    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 Nov 19 '12 at 12:14
  • I share the same init code in all our products which are WPF, Windows Service and WCF Webservice. I'll edit the setup code for our session factory into the question. – Zebi Nov 19 '12 at 12:43
  • ValidateSchema() then creates or updates the schema? Does your problem occur in all your products or is restricted to a subset? – Dirk Trilsbeek Nov 19 '12 at 15:51
  • ValidateSchema() used to update the schema but I decided to just log the required changes so they can be manually checked. I think the problem only occured in our WPF application but I can not be sure because most complaints came from other personnel in the company. They noticed it because a stored procedure blew up because NHibernate modified a random table. – Zebi Nov 19 '12 at 18:06
  • 1
    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). No idea how that would behave. – Dirk Trilsbeek Nov 19 '12 at 19:48
  • Ops, we only use ByCode mappings we completely removed Fluent a while ago but it's class map still stuck in my head ;) I corrected my question accordingly. The sessionfactory is only set up once and registered as a singleton in structure map. But the registration takes place after exporting the schema so it should not have any impact. – Zebi Nov 20 '12 at 09:01
  • from what i understand you can't reliably reproduce the error and it probably most if not all of the times occurs in an uncontrolled customer enviroment during an software upgrade. The only that i could think of right now would be the environment the application is running in. Framework version, updates, assembly versions loaded when the error occurs and the likes. What would be interesting is if you can reproduce the error in an environment where the error already occured. – Dirk Trilsbeek Nov 20 '12 at 20:57
  • Since last week our application is configured to exit with an error when the database does not match. The problem definitely occurs on my developer machine too, I think we have missed it before because it just changed the database silently or logged the schema (which was seldom noticed). If I just start up the application often (even without recompiles) the error will occur sooner or later but not reliably :( – Zebi Nov 21 '12 at 08:11
  • 2
    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 Nov 21 '12 at 09:32

1 Answers1

1

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

Community
  • 1
  • 1
tpither
  • 256
  • 1
  • 11