如何使用流畅的 NHibernate 将枚举映射为 int 值?

问题说,这一切真的,默认是为它映射为一个 string,但我需要它映射为一个 int

我目前正在使用 PersistenceModel来设置我的约定,如果这有什么不同的话。预先感谢。

更新 发现从后备箱获取最新版本的代码解决了我的问题。

26107 次浏览

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

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 */

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

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

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

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

works for me!

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

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,
}