I used this How do you map an enum as an int value with fluent NHibernate? to map in the past but I've recently upgraded to NHibernate 3 and this doesn't seem to work anymore. I've put breakpoints in my EnumConvention class and they're not being hit. The query that is hitting the database has the enum as a string which is the default configuration.
How does this work with NHibernate 3?
Update
Here is part of the mapping file that is generated:
<property name="ComponentType" type="FluentNHibernate.Mapping.GenericEnumMapper`1[[...ComponentType, ..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], FluentNHibernate, Version=1.1.0.0, Culture=neutral, PublicKeyToken=8aa435e3cb308880">
<column name="ComponentTypeId" />
</property>
It doesn't seem right that it would be using a GenericEnumMapper when an IUserTypeConvention is specified for enums.
Here is my convention:
public class EnumConvention : IUserTypeConvention
{
public void Accept( IAcceptanceCriteria<IPropertyInspector> criteria )
{
criteria.Expect( e => e.Property.PropertyType.IsEnum );
}
public void Apply( IPropertyInstance instance )
{
instance.CustomType( instance.Property.PropertyType );
}
}
Simply doing Map( m => m.MyEnum ).CustomType<MyEnum>() seems to work just fine now.
If anyone knows why IUserTypeConvention doesn't work with Fluent NHibernate in NHibernate 3, I'd still like to know why. Maybe it's because mapping the custom type to the enum works now, but why wasn't it removed from the lib then?
I'm running into a similar problem with Nhibernate 3.0GA and FluentNh (rebuild with the latest NH version). UserTypeConventions are not getting registered properly.
problem described here :
http://groups.google.com/group/nhusers/browse_thread/thread/c48da661f78bfad0
You should inherit your convention not from IUserTypeConvention, but from FluentNHibernate.Conventions.UserTypeConvention.
For example, this is the exact convention I use to map boolean and nullable booleans to a custom type called UserTrueFalseType:
/// <summary>
/// Convention: Boolean fields map to CHAR(1) T/F/Null
/// </summary>
public class BooleanTrueFalseConvention : FluentNHibernate.Conventions.UserTypeConvention<UserTrueFalseType>
{
/// <summary>
/// Accept field type criteria
/// </summary>
/// <param name="criteria"></param>
public override void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria<FluentNHibernate.Conventions.Inspections.IPropertyInspector> criteria)
{
criteria.Expect(instance =>
instance.Property.PropertyType.Equals(typeof(System.Boolean))
||
instance.Property.PropertyType.Equals(typeof(System.Nullable<System.Boolean>))
);
}
}
This works with NH 3.3 and the last version of Fluent.
Related
I am having problems with using OptimisticLock as a Convention.
However, using OptimisticLock within Individual ClassMap's works fine. It throws Stale State Object Exceptions.
Each Class corresponding to a Table in the database has a property (which corresponds to a Column in the Table) of type DateTime which I am trying to use for Locking using OptimisticLock.Version().
It works only when I use it within every ClassMap, I don't want to write so many ClassMaps, I instead want to use Auto Mapping.
It WORKS like this within the Class Map
Version(x => x.UpdTs).Column("UPD_TS");
OptimisticLock.Version();
So, I started using Convention below, but it DOESN'T WORK.
OptimisticLock.IsAny(x => x.Version());
I tried setting the DynamicUpdate, etc. Nothing seems to work for me.
Please help !
Here's what I did to get it work using a Convention :
/// <summary>
/// Class represents the Convention which defines which Property/Column serves as a part of the Optimistic Locking Mechanism.
/// </summary>
public class VersionConvention : IVersionConvention, IVersionConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IVersionInspector> criteria)
{
criteria.Expect(x => x.Name == "%COLUMN_NAME%");
}
/// <summary>
/// Method applies additional overrides to the <see cref="IVersionInstance"/>
/// </summary>
/// <param name="instance"><see cref="IVersionInstance"/></param>
public void Apply(IVersionInstance instance)
{
instance.Column("%COLUMN_NAME%");
}
}
%COLUMN_NAME% above is the Property being used for Locking using Version.
Then specified that the Version should be used for Optimistic Locking, when creating a FluentConfiguration Object, like this
OptimisticLock.Is(x => x.Version();
I have a Fluent NHibernate project I'm working on, and doing some testing I have run into a very strange error:
The entity '<>c__DisplayClass3' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).
The related entity reported is:
{Name = "<>c__DisplayClass3" FullName = "TPLLCPortal.Domain.Account+<>c__DisplayClass3"}
I don't have any class named DisplayClass, but I do have an Account entity. I'm using a primary key convention that looks like this:
public class PrimaryKeyConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.GeneratedBy.GuidComb();
}
}
My Account class inherits from an EntityBase class that declares the ID as:
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public virtual Guid Id { get; protected internal set; }
I'm confident that I'm setting up the configuration properly and that the conventions are being picked up, but just in case I added an override and specifically mapped the ID for the Account class. No dice.
Any ideas what's going on here?
I'm using FNH 1.3.0.733 with NHibernate 3.3.1.4000 (both loaded off NuGet).
Looks like I figured it out. This SO answer had the key. Because some of the methods on the class use lambdas, the compiler creates classes that you can exclude in the DefaultAutomappingConfiguration by specifying !type.IsDefined(typeof(CompilerGeneratedAttribute), false) as part of the ShouldMap override.
I have an abstract base class which inherits Sharp Arch's Entity class:
/// <summary>
/// defines an entity that will ne indexed by a search crawler and offered up as full-text searchable
/// </summary>
public abstract class IndexedEntity : Entity
{
[DocumentId]
public override int Id
{
get { return base.Id; }
protected set { base.Id = value; }
}
}
This is to a legacy db and actually the Id column is called "HelpPageID", so I have some mapping override as:
mapping.Id(x => x.Id, "HelpPageID");
The generated sql for querying HelpPage works fine when I simply inherit Entity. But inheriting IndexedEntity, when translated to sql, the column name override is ignored and instead Id is used for the column, thus failing.
Edit
Seems a general issue with an override as placing the override directly in the class has the same net effect
mapping overrides are only executed for the exact type not types which subclass the type in the mappingoverride. you have to specify an override for the subclass.
The story:
I had class User and class Organization: User. I did not use any mappings for these classes, let FNH do mapping automatically. Then, I added
public class OrganizationMap : IAutoMappingOverride<Organization>
{
public void Override(AutoMap<Organization> mapping)
{
}
}
Notice there're no overrides. So I did not expect any changes in FNH behavior. But I got this (during schema export actually):
NHibernate.MappingException:
(XmlDocument)(2,4): XML validation
error: The element 'class' in
namespace 'urn:nhibernate-mapping-2.2'
has incomplete content. List of
possible elements expected: 'meta,
subselect, cache, synchronize,
comment, tuplizer, id, composite-id'
in namespace
'urn:nhibernate-mapping-2.2'.
The generated Orders.Core.Organization.hbm.xml was really empty:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="">
<class name="Orders.Core.Organization, Orders.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Organizations" xmlns="urn:nhibernate-mapping-2.2" />
</hibernate-mapping>
So, after I reviewed the User.hbm I got the idea - I need to override Organization in the base class like this:
public class UserMap : IAutoMappingOverride<User>
{
public void Override(AutoMap<User> mapping)
{
mapping.JoinedSubClass<Organization>("ColumnId", m => {...}
);
}
}
But, I would better like to do this in a separate mapping override class for Organization... after all, what would the mapping become if I have 5 subclasses all in single Override method.
Is this possible?
Your override is telling FNH that you will manually write the mappings for that class. The error you are getting is because there is nothing being mapped for Organisation (if you look at the generated HBM.xml it will be empty).
What exactly are you wanting to write the override for?
Edit:
In that case, you can do something like this:
public class MyAlteration : IAutoMappingAlteration
{
public void Alter(AutoPersistenceModel model)
{
model.ForTypesThatDeriveFrom<User>(
map => map.HasMany<User>( x => x.Children)
);
}
}
And when configuring fluent nhibernate:
model.Alteration( a => a.Add<MyAlteration>());
Note: This is using the latest codebase of fluent nhibernate (1.0RC).
Turned out that with latest FNH (some revision after RC) this is possible now. I wonder if this is because I asked ;-)
So I had this
mapping.JoinedSubClass<Organization>("UserId", m =>
{
m.HasMany(x => x.Currencies).Element("Currency").AsBag();
}
);
and it stopped working after upgrading to RC. Then I moved this into its own class
public class OrganizationMap : IAutoMappingOverride<Organization>
{
public void Override(AutoMapping<Organization> mapping)
{
mapping.HasMany(x => x.Currencies).Element("Currency").AsBag();
}
}
it started to work again. Just like I wanted! Now I don't even need to indicate JoinedSubClass as this is the default, anyway. I can just override my subclass properties which is cool.
Though it wasn't too easy to figure out why NH started to complain about association of strings... I even thought that .Element is broken in RC. I wonder why JoinedSubClass still has this mapping part if it doesn't completely work.
Mapping a collection of enums with NHibernate
Specifically, using Attributes for the mappings.
Currently I have this working mapping the collection as type Int32 and NH seems to take care of it, but it's not exactly ideal.
The error I receive is "Unable to determine type" when trying to map the collection as of the type of the enum I am trying to map.
I found a post that said to define a class as
public class CEnumType : EnumStringType {
public CEnumType() : base(MyEnum) { }
}
and then map the enum as CEnumType, but this gives "CEnumType is not mapped" or something similar.
So has anyone got experience doing this?
So anyway, just a simple reference code snippet to give an example with
[NHibernate.Mapping.Attributes.Class(Table = "OurClass")]
public class CClass : CBaseObject
{
public enum EAction
{
do_action,
do_other_action
};
private IList<EAction> m_class_actions = new List<EAction>();
[NHibernate.Mapping.Attributes.Bag(0, Table = "ClassActions", Cascade="all", Fetch = CollectionFetchMode.Select, Lazy = false)]
[NHibernate.Mapping.Attributes.Key(1, Column = "Class_ID")]
[NHibernate.Mapping.Attributes.Element(2, Column = "EAction", Type = "Int32")]
public virtual IList<EAction> Actions
{
get { return m_class_actions; }
set { m_class_actions = value;}
}
}
So, anyone got the correct attributes for me to map this collection of enums as actual enums? It would be really nice if they were stored in the db as strings instead of ints too but it's not completely necessary.
You will need to map your CEnum type directly. In XML mappings this would mean creating a new class mapping element in your NHibernate XML mappings file.
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="YourAssembly"
auto-import="true" default-lazy="false">
...
<class name="YourAssemblyNamespace.CEnum" table="CEnumTable" mutable="false" >
<id name="Id" unsaved-value="0" column="id">
<generator class="native"/>
</id>
...
</class>
</hibernate-mapping>
To do it with attribute mappings, something like this on top of your CEnum class:
[NHibernate.Mapping.Attributes.Class(Table = "CEnumTable")] //etc as you require
This is the way i do it. There's probably an easier way but this works for me.
Edit: sorry, i overlooked that you want it as a list. I don't know how to do that...
Edit2: maybe you can map it as a protected IList[string], and convert to public IList[EAction] just as i do with a simple property.
public virtual ContractGroups Group
{
get
{
if (GroupString.IsNullOrEmpty())
return ContractGroups.Default;
return GroupString.ToEnum<ContractGroups>(); // extension method
}
set { GroupString = value.ToString(); }
}
// this is castle activerecord, you can map this property in NH mapping file as an ordinary string
[Property("`Group`", NotNull = true)]
protected virtual string GroupString
{
get;
set;
}
/// <summary>
/// Converts to an enum of type <typeparamref name="TEnum"/>.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="self">The self.</param>
/// <returns></returns>
/// <remarks>From <see href="http://www.mono-project.com/Rocks">Mono Rocks</see>.</remarks>
public static TEnum ToEnum<TEnum>(this string self)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
Argument.SelfNotNull(self);
return (TEnum)Enum.Parse(typeof(TEnum), self);
}
instead of
[NHibernate.Mapping.Attributes.Element(2, Column = "EAction", Type = "Int32")]
try
[NHibernate.Mapping.Attributes.Element(2, Column = "EAction", Type = "String")]
ie: change the Int32 to String
While I haven't tried using it myself, I stumbled across this code a little while ago and it looks pretty interesting:
http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/08/12/enumeration-classes.aspx
Like I said, I haven't used it myself, but I'm going to give it a go in a project RSN.