88

Question says it all really, the default is for it to map as a string but I need it to map as an int.

I'm currently using PersistenceModel for setting my conventions if that makes any difference.

Update Found that getting onto the latest version of the code from the trunk resolved my woes.

starball
  • 20,030
  • 7
  • 43
  • 238
Garry Shutler
  • 32,260
  • 12
  • 84
  • 119
  • 2
    Food for google bots: I was getting "illegal access to loading collection" before implementing this for my enum mapping. – 4imble Jul 16 '10 at 16:19

7 Answers7

83

The way to define this convention changed sometimes ago, it's now :

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum);
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}
Julien
  • 7,821
  • 3
  • 23
  • 16
  • 4
    This is the correct answer for the most recent version of fluent nhibernate – Sean Chambers Dec 17 '09 at 21:41
  • 1
    This looks like it works fine for all enum types, but what if you want some as strings and some as ints? I think this should be configurable at the property mapping level. – UpTheCreek Nov 12 '10 at 18:50
  • 4
    See the answer by @SztupY below that extends this to allow for nullable enums. http://stackoverflow.com/questions/439003/how-do-you-map-an-enum-as-an-int-value-with-fluent-nhibernate/2716236#2716236 – Jon Adams Jan 18 '11 at 01:03
45

So, as mentioned, getting the latest version of Fluent NHibernate off the trunk got me to where I needed to be. An example mapping for an enum with the latest code is:

Map(quote => quote.Status).CustomTypeIs(typeof(QuoteStatus));

The custom type forces it to be handled as an instance of the enum rather than using the GenericEnumMapper<TEnum>.

I'm actually considering submitting a patch to be able to change between a enum mapper that persists a string and one that persists an int as that seems like something you should be able to set as a convention.


This popped up on my recent activity and things have changed in the newer versions of Fluent NHibernate to make this easier.

To make all enums be mapped as integers you can now create a convention like so:

public class EnumConvention : IUserTypeConvention
{
    public bool Accept(IProperty target)
    {
        return target.PropertyType.IsEnum;
    }

    public void Apply(IProperty target)
    {
        target.CustomTypeIs(target.PropertyType);
    }

    public bool Accept(Type type)
    {
        return type.IsEnum;
    }
}

Then your mapping only has to be:

Map(quote => quote.Status);

You add the convention to your Fluent NHibernate mapping like so;

Fluently.Configure(nHibConfig)
    .Mappings(mappingConfiguration =>
    {
        mappingConfiguration.FluentMappings
            .ConventionDiscovery.AddFromAssemblyOf<EnumConvention>();
    })
    ./* other configuration */
Garry Shutler
  • 32,260
  • 12
  • 84
  • 119
40

Don't forget about nullable enums (like ExampleEnum? ExampleProperty)! They need to be checked separately. This is how it's done with the new FNH style configuration:

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum ||
            (x.Property.PropertyType.IsGenericType && 
             x.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
             x.Property.PropertyType.GetGenericArguments()[0].IsEnum)
            );
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}
harriyott
  • 10,505
  • 10
  • 64
  • 103
SztupY
  • 10,291
  • 8
  • 64
  • 87
  • 4
    +1 For this addition! First version doesn't work for the nullable enums (they remain as strings). – longda Aug 10 '11 at 01:38
  • @SztupY The column type in the database is `int`? And when the type accept Flags? Like: `MyEnum.Active | MyEnum.Paused` – ridermansb May 19 '13 at 12:54
  • @RidermandeSousaBarbosa: For flags check here: http://stackoverflow.com/questions/2805661/how-to-query-flags-stored-as-enum-in-nhibernate – SztupY May 28 '13 at 16:37
25

this is how I've mapped a enum property with an int value:

Map(x => x.Status).CustomType(typeof(Int32));

works for me!

Ufuk Hacıoğulları
  • 37,978
  • 12
  • 114
  • 156
Felipe
  • 259
  • 3
  • 2
  • 2
    Thanks for providing the simplest answer – Mike Jul 02 '10 at 11:48
  • My only qualm with this is that you have to remember to apply it to every enum. That's what the conventions were created for. – Garry Shutler Jul 19 '10 at 08:08
  • This works for reading but failed when I tried a criteria query. Setting up a convention (see answer to this question) did work in all cases I tried though. – Thomas Bratt Sep 29 '10 at 14:46
  • Well I thought it was great - but this will cause problems: see this post. http://nhforge.org/blogs/nhibernate/archive/2008/10/20/how-test-your-mappings-the-ghostbuster.aspx – UpTheCreek Nov 12 '10 at 18:45
  • @UpTheCreek It seems that this has been fixed and is now recommended by James Gregory from the NH team: http://www.mail-archive.com/fluent-nhibernate@googlegroups.com/msg01743.html – Ilya Kogan Jan 23 '11 at 09:52
1

For those using Fluent NHibernate with Automapping (and potentially an IoC container):

The IUserTypeConvention is as @Julien's answer above: https://stackoverflow.com/a/1706462/878612

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum);
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}

The Fluent NHibernate Automapping configuration could be configured like this:

    protected virtual ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
            .Database(SetupDatabase)
            .Mappings(mappingConfiguration =>
                {
                    mappingConfiguration.AutoMappings
                        .Add(CreateAutomappings);
                }
            ).BuildSessionFactory();
    }

    protected virtual IPersistenceConfigurer SetupDatabase()
    {
        return MsSqlConfiguration.MsSql2008.UseOuterJoin()
        .ConnectionString(x => 
             x.FromConnectionStringWithKey("AppDatabase")) // In Web.config
        .ShowSql();
    }

    protected static AutoPersistenceModel CreateAutomappings()
    {
        return AutoMap.AssemblyOf<ClassInAnAssemblyToBeMapped>(
            new EntityAutomapConfiguration())
            .Conventions.Setup(c =>
                {
                    // Other IUserTypeConvention classes here
                    c.Add<EnumConvention>();
                });
    }

*Then the CreateSessionFactory can be utilized in an IoC such as Castle Windsor (using a PersistenceFacility and installer) easily. *

    Kernel.Register(
        Component.For<ISessionFactory>()
            .UsingFactoryMethod(() => CreateSessionFactory()),
            Component.For<ISession>()
            .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
            .LifestylePerWebRequest() 
    );
Community
  • 1
  • 1
lko
  • 8,161
  • 9
  • 45
  • 62
0

You should keep the values as int / tinyint in your DB Table. For mapping your enum you need to specify mapping correctly. Please see below mapping and enum sample,

Mapping Class

public class TransactionMap : ClassMap Transaction
{
    public TransactionMap()
    {
        //Other mappings
        .....
        //Mapping for enum
        Map(x => x.Status, "Status").CustomType();

        Table("Transaction");
    }
}

Enum

public enum TransactionStatus
{
   Waiting = 1,
   Processed = 2,
   RolledBack = 3,
   Blocked = 4,
   Refunded = 5,
   AlreadyProcessed = 6,
}
Arkadas Kilic
  • 2,416
  • 2
  • 20
  • 16
0

You could create an NHibernate IUserType, and specify it using CustomTypeIs<T>() on the property map.

James Gregory
  • 14,173
  • 2
  • 42
  • 60