Search code examples
c#nhibernatenhibernate-mappingnhibernate-mapping-by-code

Set scale and precision of decimal identifier


We are working on a distributed, sequential ID generator that creates a 96-bit numeric ID. The datatype we work in the application for this value is decimal. On the database side, the column's datatype would be decimal(28,0).

The problem is that when I map by code and configure the Id, I can't specify the precision and scale. Ideally, I'd want something like this:

map.Id(c => c.Id, id =>
         {
             id.Generator(RustFlakeGeneratorDef.Instance);
             id.Precision(28); // nope
             id.Scale(0); // nope. *$%!
         });

If I try and ignore that, NHibernate falls back to using decimal(28,5) when creating parameters and issuing commands, which won't work.

I'm aware that there's a ticket, and if I'd like to contribute to that in due time. In the meantime, I need to see if this is going to pan out, so I'm wondering if there are any workarounds for this. Custom type, hack the deserialized HBM, anything like that?

I did try to create my own type that derives from DecimalType, and I overrode the property that returns the SqlType, where I specified my own precision and scale, but that didn't seem to do anything.


Solution

  • So I figured that I can edit the deserialized HBM mappings that ByCode creates and update the mappings manually.

    var mapping = ModelMapper.CompileMappingFor(entityTypes);
    
    // HACK: by code mapping doesn't support setting precision and scale, so fix flake ID columns here
    var flakeTypeName = typeof (RustFlakeGenerator).AssemblyQualifiedName;
    
    foreach (var id in (from root in mapping.RootClasses
                        where root.Id.Type.name == "Decimal" && root.Id.generator.@class == flakeTypeName
                        select root.Id))
    {
        var column = id.column == null || !id.column.Any() ? new HbmColumn() : id.column.First();
        column.name = id.name;
        column.precision = "28";
        column.scale = "0";
    
        if (id.column == null || !id.column.Any()) id.column = new[] {column};
    }
    

    It sucks, and SchemaExport et al totally ignore the precision and scale (defaulting to decimal(19,5), but I only use that for scaffolding and can just fix that manually), but it works.