Reputation: 4402
I'm trying to figure out how to map an IDictionary property in fluent 1.0 RTM.
From my understanding this translates to a ternary association.
Example:
class Bar
{
public IDictionary<SomeEntity, int> Foo {get; set;}
}
Bar.hbm.xml would then contain:
<map name="Foo" table="BarFooTable">
<key column="..."/>
<index-many-to-many class="SomeEntity" column="SomeEntity_Id"/>
<element column="Value" type="int"/>
</map>
What would I have to write in fluent nhibernate to produce this mapping xml?
The point of interest here is that the key is an entity type while the value is a value type. (edit: At least, this seems to distinguish itself from the various other examples and questions floating around on stackoverflow or google groups, which are value-value or key-key)
After much experimentation I can produce a mapping for an IDictionary<SomeEntity,SomeEntity>
(pure entity types):
HasManyToMany(x => x.Foo)
.AsMap("Key")
.AsTernaryAssociation("Key2", "Value")
;
I can also produce a mapping for an IDictionary<int,int>
(pure value types):
HasMany(x => x.Foo)
.AsMap<int>("Key")
.Element("Value")
;
I can even get some way to producing a mapping for an IDictionary<int, SomeValue)
, although none that NHibernate will accept.
But I cannot figure out how to produce a mapping for an IDictionary<SomeValue, int>
which is what I want. Can someone provide some tips?
Upvotes: 5
Views: 1592
Reputation: 1023
Given these (simplified) classes:
class LetterTemplate
{
IDictionary<State, FieldAccessOptionality> StateAccess {get; protected set;}
}
class State
{
}
enum FieldAccessOptionality
{
}
This worked for me.
(Yes I know the value is an Enum, but the principle is the same as an int).
HasManyToMany(x => x.StateAccess)
.ParentKeyColumn("`letter_template`")
.ChildKeyColumn("`state`")
.AsMap<State>("`state`")
.Element("`access`",
p =>
p.Type<IntEnumType<FieldAccessOptionality>>())
.AsTernaryAssociation("`state`", "`access`")
.Table("`letter_template_state_access`");
Note the IntEnumType<> class that converts from an enum to an int for NHibernate. If you were mapping an int instead of an enum, you could just use the standard NHibernate Int32Type IUserType. The full listing for this class is as follows:
public class IntEnumType<T> : IUserType where T : struct
{
public object Assemble(object cached, object owner)
{
return cached;
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return value;
}
public new bool Equals(object a, object b)
{
return Object.Equals(a, b);
}
public int GetHashCode(object x)
{
if (x == null)
return 0;
else
return x.GetHashCode();
}
public bool IsMutable
{
get { return false; }
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
object result = NHibernateUtil.Int32.NullSafeGet(rs, names);
if (result == null)
{
return null;
}
else
{
Type typ = Enum.GetUnderlyingType(typeof(T));
result = Convert.ChangeType(result, typ);
return Enum.ToObject(typeof(T), result);
}
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
else
((IDataParameter)cmd.Parameters[index]).Value = Convert.ChangeType(value, typeof(int));
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType
{
get { return typeof(T); }
}
public SqlType[] SqlTypes
{
get
{
SqlType type = new SqlType(DbType.Int32);
return new SqlType[] { type };
}
}
}
Upvotes: 0
Reputation: 2406
HasMany(x => x.Foo)
.KeyColumn("BarId")
.Element("IntValue")
.AsMap<SomeEntity>("SomeEntityId")
.AsTernaryAssociation("SomeEntityId");
Upvotes: 2
Reputation:
I ran into the same problem, and wasn't happy with mixing fluent and hbm mappings. You can see my fix here.
Upvotes: 1
Reputation: 1390
It seems we have the same problem:
How to map this in Fluent.NHibernate
I just used hbm.xml (that was generated by Fluent.Nhibernate in my project) I modified it a little oufcourse. If you set
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<DomainClass>()
.ExportTo("Path");
m.HbmMappings.AddFromAssemblyOf<DomainClass>();
})
and if you have both ClassMap and hbm.xml the hbm.xml should override the ClassMap, so you'll be fine untill it's fixed.
I also need natural-id for my class and it also isn't supported with Fluent.Nhibernate, so I had no choice but to use hbm.xml
Upvotes: 0