Mapping NullReferenceException to enum using free NHibernate automaton
I have a class that contains a set of enums as follows.
public enum TransactionType
{
...
}
public class PaymentMethod
{
...
public virtual IList<TransactionType> SupportedTransactionTypes { get; set; }
}
The other TransactionType enumeration references work correctly, but with this collection I get an exception: "NHibernate.MappingException: association reference: unmapped class: mynamespace.TransactionType".
Looking around, it seems that I need to specify the display type of the items, i.e. one-to-many, item, or composite item.
I have the following overridden mappings set up for the PaymentMethod class:
mapping.HasMany(x => x.TransactionTypes)
.Element("TransactionTypeId"), x => x.Type<TransactionType>());
But this throws the following exception ...
Verification failed: System.NullReferenceException: Object reference not set to object instance. at FluentNHibernate.Conventions.Inspections.OneToManyInspector.get_Class () at e: \ horn.horn \ orm \ fluentnhibernate \ Working \ src \ FluentNHibernate \ Conventions \ Inspections \ OneToManyInspector.cs: line 40 at FluentNHibernate.Convention.Application instance ICollectionInstance) at e: \ horn.horn \ orm \ fluentnhibernate \ Working \ src \ FluentNHibernate \ Conventions \ ProxyConvention.cs: line 79 at FluentNHibernate.Visitors.ConventionVisitor.Apply [TInspector, TInstance] (IEnumerable conventions, TInstance instance) at : \ horn.horn \ orm \ fluentnhibernate \ Working \ src \ FluentNHibernate \ Visitors \ ConventionVisitor.cs: line 269 when ...
I've searched for many different display options including TableName, KeyColumn and whatever I can think of, but I can't seem to get this mapping to work.
Any help is appreciated ...
a source to share
This may be a recent fix in FluentNHibernate, but this works with FluentNH v1.2.0.712. I'm pretty sure NHibernate with normal * .hbm.xml mappings has supported this type of mapping for years.
This is the auto override that worked for me:
mapping.HasMany(x => x.SupportedTransactionTypes)
.Element("TransactionTypeId");
... which leads to this XML ...
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" name="so.Q2676867.PaymentMethod, so, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`PaymentMethod`">
<id access="backfield" name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Id" />
<generator class="identity" />
</id>
<bag name="SupportedTransactionTypes">
<key>
<column name="PaymentMethod_id" />
</key>
<element type="so.Q2676867.TransactionType, so, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<column name="TransactionTypeId" />
</element>
</bag>
</class>
</hibernate-mapping>
... and these tables:
create table [PaymentMethod] (
Id INT IDENTITY NOT NULL,
primary key (Id)
)
create table SupportedTransactionTypes (
PaymentMethod_id INT not null,
TransactionTypeId INT null
)
alter table SupportedTransactionTypes
add constraint FK738E3751B597A1C
foreign key (PaymentMethod_id)
references [PaymentMethod]
... this is exactly what I would expect. Yay NHibernate!
a source to share
You can store the collection in the database as a pipe delimited string ...
protected string _enumCollection = "";
public virtual ISet<MyEnum> EnumCollection
{
get
{
var set = new HashedSet<MyEnum>();
if (string.IsNullOrEmpty(_enumString))
return set;
_enumCollection.Split(new[] {"|"}, StringSplitOptions.None).ToList()
.ForEach(
x => set.Add((MyEnum)(Int32.Parse(x)))
);
return new HashedSet<MyEnum>(set);
}
set { _enumCollection = string.Join("|", value.Select(x => ((int)x).ToString()).ToArray()); }
}
and then go to the line support box:
Map(x => x.EnumCollection).CustomType(typeof(string)).Access.CamelCaseField(Prefix.Underscore);
You need to use helper methods to add / remove enums, not use methods of the collection itself to update the backing field.
public virtual void AddEnum(MyEnum enum)
{
if (!EnumCollection.Contains(enum))
{
var set = EnumCollection; //you need to get the collection
set.Add(enum); //add enum to it
EnumCollection= set; //then set the set again
}
}
public virtual void RemoveEnum(MyEnum enum)
{
if (EnumCollection.Contains(enum))
{
var set = EnumCollection; //get collection
set.Remove(enum); //add enum
EnumCollection= set; //re-set collection
}
}
Hope it helps.
a source to share