I'm mapping by code using NHibernate. Here is the class:
[Serializable]
public class Person : ObjectBase
{
public virtual IDictionary<AttributeType, Attribute> Attributes { get; set; }
}
and here is the mapping:
public class PersonMapping : BaseObjectMapping<Person>
{
public PersonMapping()
{
Map(x => x.Attributes, c =>
{
c.Access(Accessor.Property);
c.Lazy(CollectionLazy.NoLazy);
c.Cascade(Cascade.All);
c.Key(k => k.Column("PersonId"));
}, k => k.Element(m => m.Column("AttributeTypeId")), r => r.OneToMany());
}
}
The problem is, when I go and attempt to access the keys in the dictionary, the key type is AttributeTypeProxy. Then, it seems to attempt to lazy load the key in an area of code where the session no longer exists. Thus, throwing the error: Initializing[AttributeType#1]-Could not initialize proxy - no Session.
So, after some research, I'm told I need to force it to eager load. I do not see any .Lazy related to the key mappings. Also, I've verified that the value of the Map is eager loaded (I assume this is due to the CollectionLazy.NoLazy). How do I make the key of a Map eager load?
Edit:
In an attempt to produce it not loading Attributes properly as per Rippo's answer, I changed the mapping for Person temporarily to produce the Attribute table.
Map(x => x.Attributes, c =>
{
c.Key(k => k.Column("PersonId"));
c.Table("Attribute");
},
k => k.Element(m => m.Column("AttributeTypeId")),
r => r.Component(m => m.Property(p => p.Data)));
Now, when you try to access, as per his example, person.Attributes
, it gives this error: Initializing[Person#1]-failed to lazily initialize a collection of role: Person.Attributes, no session or session was closed
Rippo also suggested posting code that actually retrieves the data. I use these methods:
internal static class RepositoryHelper
{
public static void PerformDatabaseUpdate(Action<ISession> action)
{
using (var session = NHibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
action(session);
transaction.Commit();
}
}
public static T PerformDatabaseQuery<T>(Func<ISession, T> action)
{
using (var session = NHibernateHelper.OpenSession())
{
return action(session);
}
}
}
I haven't had any problems using these prior to trying to use a Map for mapping an IDictionary. Though, before this, I had been only using properties and ICollections (via Set mapping). I should also mention that this is used for a client-server application. The client is a MVVM WPF application and the server will be a service (currently, just a console app).
Edit2:
I've found a workaround, but I would definitely not consider it an answer. I also have no idea why it works. The only conclusion I can come up with is that the Map in mapping by code isn't doing its job. Here is what I changed:
[Serializable]
public class Person : ObjectBase
{
public virtual IDictionary<AttributeType, Attribute> Attributes { get; set; }
public virtual ICollection<AttributeType> AttributeTypes
{
get { return Attributes.Keys; }
set { }
}
}
Yeah. I added an ICollection of AttributeType to also hold the Keys from the dictionary. It can only return the actual keys from Attributes; though, I need the set method for NHibernate to use it properly. I simply put nothing in the method.
Then, I added this to the mapping:
public class PersonMapping : BaseObjectMapping<Person>
{
public PersonMapping()
{
Map(x => x.Attributes, c =>
{
c.Access(Accessor.Property);
c.Lazy(CollectionLazy.NoLazy);
c.Cascade(Cascade.All);
c.Key(k => k.Column("PersonId"));
}, k => k.Element(m => m.Column("AttributeTypeId")), r => r.OneToMany());
Set(x => x.AttributeTypes, c =>
{
c.Table("UnusedAttributeTypes");
c.Access(Accessor.Property);
c.Lazy(CollectionLazy.NoLazy);
c.Key(k => k.Column("PersonId"));
}, r => r.ManyToMany());
}
}
This is simply creating a table called UnusedAttributeTypes that contains the PersonId and the AttributeTypeId. It is a dummy table since I can't access via my objects.
Now, when I go and call person.Attributes.Keys
, they are not proxy AttributeType object, but the actual objects and they are populated properly. Also, the person.Attributes.Values
are still populated as they have been. No change there.
I hope I don't need to search through the NHibernate source code to figure out why this solves the issue, or what the actual issue is.
Edit 3: Removed the c.Cascade(Cascade.All); from the AttributeTypes mapping.
Map(...).Not.Lazy();
? – Andre Calil