Enum to integer mapping causing updates on every flush
I am trying to map an enum property (instance of System.DayOfWeek) in my model to an integer database field. Other enum properties in the model should b开发者_如何学Ce mapped to strings, so I don't wish to define a convention.
I understand that this should be possible using a fluent mapping like:
Map(x => x.DayOfWeek).CustomType<int>();
and indeed, at first glance this appears to be working.
However, I noticed that instances of entities with properties mapped in this way are being updated every time the session was flushed, even though no amendments have been made to them.
To find out what is causing this flush, I set up an IPreUpdateEventListener, and inspected the OldState and State of the entity. See the attached image. In the OldState, the relevant object is an int, whereas in State it is a DayOfWeek.
If I use an HBM XML mapping with no type attribute specified, this issue doesn't arise.
So...
Is this a bug or shortcoming in the GenericEnumMapper? Is there any way of telling the FNH mapping not to specify any type attribute on the generated HBM? If not, can I specify the default type that NH uses for enums (and what is that)?
If you use my enum convention you don't have that problem.
public class EnumConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Apply(IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType == typeof(AddressType) ||
x.Property.PropertyType == typeof(Status) ||
x.Property.PropertyType == typeof(DayOfWeek));
}
}
You can then map your property like regular:
Map(x => x.DayOfWeek);
EDIT : Updated the convention to pick specific enums to use for the int conversion. All enums that are not checked here would be mapped as string. You might have to experiment a little with what to actually test against. I am unsure if the propertytype will do it directly.
I know I'm late to the party - it's been two years since the question. But since I've stumbled upon this, I might just add solved the problem for me:
Map(x => x.DayOfWeek).CustomType<enumType>();
It did the trick for me: it stopped updating every time.
Source: https://groups.google.com/forum/#!searchin/fluent-nhibernate/enum/fluent-nhibernate/bBXlDRvphDw/AFnYs9ei7O0J
One workaround that I use is to have an int backing field and letting NHibernate use that for mapping.
Whenever NHibernate has to do a cast to compare the new value with the old one - it is always marked as dirty - causing the flush.
Simple way that worked for me was to change mapping's custom type from int
to PersistentEnumType
. Make sure to declare a generic version to make your life easier:
public class PersistentEnumType<T> : PersistentEnumType {
public PersistentEnumType() : base(typeof(T)) {}
}
Then use
Map(x => x.DayOfWeek)
.CustomType<PersistentEnumType<System.DayOfWeek>>();
This does not require changes to your entities, only mappings, and can be applied on a per-property basis.
See more here.
It depends on if you need to have DayOfWeek specifically as an integer.
If you're casting as part of the mapping, equality will always fail, and the property will be marked as dirty.
I'd probably map:
Map(x => x.DayOfWeek).CustomType();
and create a read only property that presents the DayOfWeek value as an integer if it's really required. Regardless, mapping as the actual type should work and prevent false-dirty.
You might consider an alternate approach; I have found the usage of Fabio Maulo's well known instance types to be invaluable for such uses. The benefit of these is immediately apparent any time you find yourself trying to extend what a basic enum can do (eg. providing a localized description, etc.)
精彩评论