在NHibernate中,如果一个实体被一个查询延迟加载,然后请求在同一个会话中被第二个查询急切加载,则第二个查询中返回的类型总是一个代理(我相信一个缓存)来自第一个查询的代理)。这可以在这个高度简化的英国例子中得到证明:
public class CarClassMap : ClassMap<Car>
{
public CarClassMap()
{
this.Id(x => x.Id);
this.Map(x => x.Model);
// note: lazy load by default
this.References(x => x.Colour).Column("ColourId").LazyLoad();
}
}
public class ColourClassMap : ClassMap<Colour>
{
public ColourClassMap()
{
this.Id(x => x.Id);
this.Map(x => x.Description);
}
}
public static void Main()
{
using (var session = CreateSessionFactory().OpenSession())
{
// note: both cars are the same colour in the database
var jaguar = session.QueryOver<Car>()
.Where(x => x.Id == 1)
.FutureValue()
.Value;
Console.WriteLine("Jaguar colour type=" + jaguar.Colour.GetType());
var aston =
session.QueryOver<Car>()
.Fetch(x => x.Colour).Eager //note: eager load
.Where(x => x.Id == 2)
.FutureValue()
.Value;
Console.WriteLine("Aston Martin colour type=" + aston.Colour.GetType());
}
Console.Read();
}
该程序的输出是:
Jaguar colour type=ColourProxy
Aston Martin colour type=ColourProxy
两个'Color'属性都是代理,尽管第二个查询请求急切加载。但是,当运行时,只需 eager-load查询:
public static void Main()
{
using (var session = CreateSessionFactory().OpenSession())
{
var aston =
session.QueryOver<Car>()
.Fetch(x => x.Colour).Eager
.Where(x => x.Id == 2)
.FutureValue()
.Value;
Console.WriteLine("Aston Martin colour type=" + aston.Colour.GetType());
}
Console.Read();
}
输出结果为:
Aston Martin colour type=TestApp.Colour
具体的基础类型。
在我们的真实系统中,返回的对象被传递给执行一些复杂逻辑的映射层。这种差异导致我们出现问题,因为根据先前在会话中发出的查询,属性的输入方式不同。
基本上问题是我们如何避免产生代理的急切负载请求,而是将其强制转换为具体类型?我们知道我们可以使用NHibernate实用程序手动“取消代理”对象,但我们不希望每次查询该实体时都这样做。如果可能的话,我们更喜欢在类地图中做到这一点。或者是否有更好的解决方案?感谢。
答案 0 :(得分:1)
NHibernate默认保证会话为同一实体返回的实例的唯一性。这就是为什么你的eager load返回先前加载的延迟代理,如果实体的同一个会话中有一个(顺便说一下,那么应该完全初始化)。
根据您的应用程序的工作方式,您可能很难躲避这种情况。
您可以在急切加载之前Clear
会话以避免这种情况,但是这将取消所有挂起的更改,并且当任何先前加载的实体与会话分离时,任何未加载的代理都将无法使用。
如果你事先有参考,你可以改为Evict
你的实体,但在阅读你的问题时看起来并不是这样。
我宁愿调整映射层以支持获取代理或基本实体类。这个question与此有关,并且有很多有趣的答案。
在您的情况下,Diego Mijelshon answer's linked blog可以很好地让您的地图图层始终获得具体的类。
它包括向您的实体类添加一个属性,它将为您提供具体的实例,无论您是否拥有它,或者您拥有代理。
public virtual object Actual { get { return this; } }
正如他警告的那样,这是一个黑客攻击。通过此方法获得的具体实例之后不应与NHibernate会话一起使用。