我可以强制 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一起使用了。