7

I understand that in NHibernate, using mapping by code, I can specify the precision and scale of a decimal property like so:

Property(
    x => x.Dollars,
    m =>
        {
            m.Precision(9);
            m.Scale(6);
        }
 );

That is nice, but I was wondering if there was a way that I could easily map ALL of the decimal properties in all of the classes in an easy way. It seems kind of crazy that I would have to go through all of my mappings and update each of them by hand. Does anyone know how this can be achieved ?

A.R.
  • 15,405
  • 19
  • 77
  • 123

3 Answers3

4

Use the BeforeMapProperty on the ModelMapper:-

var mapper = new ModelMapper();

mapper.BeforeMapProperty += (inspector, member, customizer) =>  {
    if (member.LocalMember.GetPropertyOrFieldType() == typeof (decimal))
    {
      customizer.Precision(9);
      customizer.Scale(6);
    }
};

The only other thing to add is remove all occurrences of:-

 m => { m.Precision(9); m.Scale(6); }

from your mapping classes as these will override your convention set in BeforeMapProperty unless you have other decimals that have different scales or precisions.

Rippo
  • 22,117
  • 14
  • 78
  • 117
3

You could write a UserType. The advantage is that you could easily distinguish different types of decimals (you most probably don't want to have the same precision for all decimals).

Property(
    x => x.Dollars,
    m => m.Type<MoneyUserType>()
 );

You have some effort to put this into all monetary properties, but then you have a more readable and self descriptive mapping definition.


A similar solution (syntactically), but easier to implement, is to write an extension method which sets up the precision.

Property(
    x => x.Dollars,
    m => m.MapMoney()
 );

public static void MapMoney(this IPropertyMapper mapper)
{
    m.Precision(9);
    m.Scale(6);
}

Same here: it makes the mapping definition more self descriptive.

(Yes I know that you don't want to change all the files, but I still recommend to put this information into the mapping files, because it is more explicit what the decimal actually is. It is very easy to change the mapping for all Money properties, but keep the Amount properties. For the completely implicit solution, read on.)


Alternatively you could use mapping conventions. These is very powerful. You still can overwrite the precision in the mapping file, which provides great flexibility.

mapper.BeforeMapProperty += MapperOnBeforeMapProperty;


private void MapperOnBeforeMapProperty(
    IModelInspector modelInspector,
    PropertyPath member,
    IPropertyMapper propertyCustomizer)
{
    Type propertyType;
    // requires some more condition to check if it is a property mapping or field mapping
    propertyType = (PropertyInfo)member.LocalMember.PropertyType;
    if (propertyType == typeof(decimal)) 
    {
        propertyCustomizer.Precision(9);
        propertyCustomizer.Scale(6);
    }
}

It is also possible to put the user type into the mapping convention, as a default.

Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • I gave Rippo the points because I read his first, and it is more concise. I really wish I could upvote this one twice or three times though, very informative. – A.R. Apr 11 '13 at 12:47
  • Don't worry +1 from me as well – Rippo Apr 11 '13 at 12:48
  • Rippo deserves that his answer was accepted, because he actually answered the question (also much quicker than I did). I proposed something else, I'm glad that you like it ... – Stefan Steinegger Apr 11 '13 at 14:24
  • I never thought about the second option (extension method) before but that would help if I had say 3+ more types with different settings. – Rippo Apr 11 '13 at 14:28
0

Can you use FluentNHibernate? It enables you to apply conventions with as much flexibility as you need. See here: https://github.com/jagregory/fluent-nhibernate/wiki/Conventions and here: http://marcinobel.com/index.php/fluent-nhibernate-conventions-examples/ where it has this particular example:

public class StringColumnLengthConvention
    : IPropertyConvention, IPropertyConventionAcceptance
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Type == typeof(string))
            .Expect(x => x.Length == 0);
    }
 
    public void Apply(IPropertyInstance instance)
    {
        instance.Length(100);
    }
}

This looks like you could easily adapt to map all decimals like he does with strings.

Alvaro Rodriguez
  • 2,820
  • 2
  • 33
  • 38
  • I'd rather not. There are a few dozen classes mapped, and it would be easier to just re-map my decimals than changing over to Fluent. Thanks though. – A.R. Apr 11 '13 at 12:26