Free nHibernate, Hi-Lo table with entity per row using convention

Is there a way to specify a table to use for Hi-Lo values, with every object having an entry in a row through convention (at the same time that nHibernate creates the table structure for you)? I would like to reproduce what Phil Haydon wrote about here , but without manually managing the table. Be that as it may, transferring the row code to the table in its own agreement will only work if you have already created the appropriate entries for the "TableKey" in the table.

Alternatively, is this possible with XML mappings?

And if all else fails, this is the only other suitable option for using a custom generator, aa this post ?

+5
source share
3 answers

Fabio Maulo spoke about this in one of his position reports.

Code example matching:

mapper.BeforeMapClass += (mi, type, map) =>
    map.Id(idmap => idmap.Generator(Generators.HighLow,
        gmap => gmap.Params(new
        {
            table = "NextHighValues",
            column = "NextHigh",
            max_lo = 100,
            where = string.Format(
                "EntityName = '{0}'", type.Name.ToLowerInvariant())
        })));

For FluentNHibernate you can do something like:

public class PrimaryKeyConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        var type = instance.EntityType.Name;
        instance.Column(type + "Id");
        instance.GeneratedBy.HiLo(type, "NextHigh", "100", 
            x => x.AddParam("where", String.Format("EntityName = '{0}'", type));
    }
}

In addition, Fabio explained how you could use IAuxiliaryDatabaseObjectto create a Hi-Lo script.

private static IAuxiliaryDatabaseObject CreateHighLowScript(
    IModelInspector inspector, IEnumerable<Type> entities)
{
    var script = new StringBuilder(3072);
    script.AppendLine("DELETE FROM NextHighValues;");
    script.AppendLine(
        "ALTER TABLE NextHighValues ADD EntityName VARCHAR(128) NOT NULL;");
    script.AppendLine(
        "CREATE NONCLUSTERED INDEX IdxNextHighValuesEntity ON NextHighValues " 
        + "(EntityName ASC);");
    script.AppendLine("GO");

    foreach (var entity in entities.Where(x => inspector.IsRootEntity(x)))
    {
        script.AppendLine(string.Format(
         "INSERT INTO [NextHighValues] (EntityName, NextHigh) VALUES ('{0}',1);",
         entity.Name.ToLowerInvariant()));
    }

    return new SimpleAuxiliaryDatabaseObject(
        script.ToString(), null, new HashedSet<string> {
           typeof(MsSql2005Dialect).FullName, typeof(MsSql2008Dialect).FullName 
        });
}

You would use it as follows:

configuration.AddAuxiliaryDatabaseObject(CreateHighLowScript(
    modelInspector, Assembly.GetExecutingAssembly().GetExportedTypes()));
+8
source

For Fluent users, NHibernate Anthony Dewhirst has posted a good solution here: http://www.anthonydewhirst.blogspot.co.uk/2012/02/fluent-nhibernate-solution-to-enable.html

+2

Creating Anthony Dewhirst is already a great solution, I ended up with the following, which adds several improvements:

  • Adds acceptance criteria so that it does not try to process non-integral types of identifiers (for example, Guid) and does not stomp on Id mappings that explicitly have a generator installed
  • Script generation takes into account the dialect
public class HiLoIdGeneratorConvention : IIdConvention, IIdConventionAcceptance
{
    public const string EntityColumnName = "entity";
    public const string MaxLo = "500";

    public void Accept(IAcceptanceCriteria<IIdentityInspector> criteria)
    {
        criteria
            .Expect(x => x.Type == typeof(int) || x.Type == typeof(uint) || x.Type == typeof(long) || x.Type == typeof(ulong)) // HiLo only works with integral types
            .Expect(x => x.Generator.EntityType == null); // Specific generator has not been mapped
    }

    public void Apply(IIdentityInstance instance)
    {
        instance.GeneratedBy.HiLo(TableGenerator.DefaultTableName, TableGenerator.DefaultColumnName, MaxLo,
                                  builder => builder.AddParam(TableGenerator.Where, string.Format("{0} = '{1}'", EntityColumnName, instance.EntityType.FullName)));
    }

    public static void CreateHighLowScript(NHibernate.Cfg.Configuration config)
    {
        var dialect = Activator.CreateInstance(Type.GetType(config.GetProperty(NHibernate.Cfg.Environment.Dialect))) as Dialect;
        var script = new StringBuilder();

        script.AppendFormat("DELETE FROM {0};", TableGenerator.DefaultTableName);
        script.AppendLine();
        script.AppendFormat("ALTER TABLE {0} {1} {2} {3} NOT NULL;", TableGenerator.DefaultTableName, dialect.AddColumnString, EntityColumnName, dialect.GetTypeName(SqlTypeFactory.GetAnsiString(128)));
        script.AppendLine();
        script.AppendFormat("CREATE NONCLUSTERED INDEX IX_{0}_{1} ON {0} ({1} ASC);", TableGenerator.DefaultTableName, EntityColumnName);
        script.AppendLine();
        if (dialect.SupportsSqlBatches)
        {
            script.AppendLine("GO");
            script.AppendLine();
        }
        foreach (var entityName in config.ClassMappings.Select(m => m.EntityName).Distinct())
        {
            script.AppendFormat("INSERT INTO [{0}] ({1}, {2}) VALUES ('{3}',1);", TableGenerator.DefaultTableName, EntityColumnName, TableGenerator.DefaultColumnName, entityName);
            script.AppendLine();
        }
        if (dialect.SupportsSqlBatches)
        {
            script.AppendLine("GO");
            script.AppendLine();
        }

        config.AddAuxiliaryDatabaseObject(new SimpleAuxiliaryDatabaseObject(script.ToString(), null));
    }
}
+2
source

All Articles