我正在使用 NHibernate 通过代码进行映射。这是课程:
[Serializable]
public class Person : ObjectBase
{
public virtual IDictionary<AttributeType, Attribute> Attributes { get; set; }
}
这是映射:
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());
}
}
问题是,当我尝试访问字典中的键时,键类型是 AttributeTypeProxy。然后,它似乎试图在会话不再存在的代码区域中延迟加载密钥。因此,抛出错误:Initializing[AttributeType#1]-Could not initialize proxy - no Session。
因此,经过一些研究,我被告知我需要强制它急切加载。我没有看到任何与键映射相关的 .Lazy。另外,我已经验证了 Map 的值是预先加载的(我假设这是由于 CollectionLazy.NoLazy)。如何使 Map 急切加载的密钥?
编辑:
为了使其无法按照 Rippo 的回答正确加载属性,我临时更改了 Person 的映射以生成 Attribute 表。
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)));
现在,当您按照他的示例尝试访问时person.Attributes
,它会给出以下错误:Initializing[Person#1]-failed to lazily initialize a collection of role: Person.Attributes, no session or session was closed
Rippo 还建议发布实际检索数据的代码。我使用这些方法:
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);
}
}
}
在尝试使用 Map 映射 IDictionary 之前,我在使用这些之前没有遇到任何问题。不过,在此之前,我一直只使用属性和 ICollections(通过 Set 映射)。我还应该提到,这用于客户端-服务器应用程序。客户端是一个 MVVM WPF 应用程序,而服务器是一个服务(目前,只是一个控制台应用程序)。
编辑2:
我找到了一种解决方法,但我绝对不会认为这是一个答案。我也不知道它为什么起作用。我能得出的唯一结论是,通过代码映射的 Map 没有发挥作用。这是我所做的更改:
[Serializable]
public class Person : ObjectBase
{
public virtual IDictionary<AttributeType, Attribute> Attributes { get; set; }
public virtual ICollection<AttributeType> AttributeTypes
{
get { return Attributes.Keys; }
set { }
}
}
是的。我添加了一个 AttributeType 的 ICollection 来保存字典中的键。它只能从 Attributes 返回实际的键;不过,我需要 NHibernate 的 set 方法才能正确使用它。我只是在方法中什么都没放。
然后,我将其添加到映射中:
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());
}
}
这只是创建一个名为 UnusedAttributeTypes 的表,其中包含 PersonId 和 AttributeTypeId。这是一个虚拟表,因为我无法通过我的对象访问。
现在,当我去调用时person.Attributes.Keys
,它们不是代理 AttributeType 对象,而是实际对象并且它们被正确填充。此外,person.Attributes.Values
他们仍然像以前一样居住。那里没有变化。
我希望我不需要搜索 NHibernate 源代码来找出解决问题的原因,或者实际问题是什么。
编辑 3:删除 c.Cascade(Cascade.All); 来自 AttributeTypes 映射。