我可以强制 NHibernate 完全加载以前在同一会话中代理加载的对象吗?
Can I force NHibernate to fully load an object that was previously proxy-loaded in the same Session?
在 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
两个 'Colour' 属性都是代理,尽管第二个查询请求预先加载。但是,当 运行 只是 急切加载查询时:
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 实用程序手动 'un-proxy' 对象,但我们宁愿不必在每次查询该实体时都这样做。如果可能的话,我们更希望在 class 地图中使用一种方法。还是有更好的解决方案?谢谢
NHibernate 默认保证同一实体的会话 returns 实例的唯一性。这就是为什么如果实体的同一个会话中有一个惰性代理(顺便说一下,它应该已经完全初始化),那么你急切地加载 returns 之前加载的惰性代理。
您可能很难避免这种情况,具体取决于您的应用程序的工作方式。
您可以 Clear
在急切加载之前的会话以避免这种情况,但这将取消所有未决更改并使任何卸载的代理不可用,同时任何先前加载的实体将从会话中分离。
如果您事先有对实体的引用,您可以 Evict
只有您的实体,但在阅读您的问题时看起来并非如此。
我宁愿调整映射层以支持获取代理或基础实体 class。 question 就是关于这个的,并且有很多有趣的答案。
在您的情况下,Diego Mijelshon answer's linked blog 可能很适合您,因为它允许您的映射层始终获得具体 class。
它包括向您的实体 class 添加一个 属性,无论您已经拥有它还是拥有代理,它都会为您提供具体的实例。
public virtual object Actual { get { return this; } }
正如他所警告的那样,这是一种黑客行为。通过该方法得到的具体实例以后不能再和NHibernate session一起使用了。
在 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
两个 'Colour' 属性都是代理,尽管第二个查询请求预先加载。但是,当 运行 只是 急切加载查询时:
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 实用程序手动 'un-proxy' 对象,但我们宁愿不必在每次查询该实体时都这样做。如果可能的话,我们更希望在 class 地图中使用一种方法。还是有更好的解决方案?谢谢
NHibernate 默认保证同一实体的会话 returns 实例的唯一性。这就是为什么如果实体的同一个会话中有一个惰性代理(顺便说一下,它应该已经完全初始化),那么你急切地加载 returns 之前加载的惰性代理。
您可能很难避免这种情况,具体取决于您的应用程序的工作方式。
您可以 Clear
在急切加载之前的会话以避免这种情况,但这将取消所有未决更改并使任何卸载的代理不可用,同时任何先前加载的实体将从会话中分离。
如果您事先有对实体的引用,您可以 Evict
只有您的实体,但在阅读您的问题时看起来并非如此。
我宁愿调整映射层以支持获取代理或基础实体 class。 question 就是关于这个的,并且有很多有趣的答案。
在您的情况下,Diego Mijelshon answer's linked blog 可能很适合您,因为它允许您的映射层始终获得具体 class。
它包括向您的实体 class 添加一个 属性,无论您已经拥有它还是拥有代理,它都会为您提供具体的实例。
public virtual object Actual { get { return this; } }
正如他所警告的那样,这是一种黑客行为。通过该方法得到的具体实例以后不能再和NHibernate session一起使用了。