我可以强制NHibernate完全加载先前在同一个Session中加载代理的对象吗?

时间:2017-01-27 16:56:33

标签: c# nhibernate fluent-nhibernate

在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实用程序手动“取消代理”对象,但我们不希望每次查询该实体时都这样做。如果可能的话,我们更喜欢在类地图中做到这一点。或者是否有更好的解决方案?感谢。

1 个答案:

答案 0 :(得分:1)

NHibernate默认保​​证会话为同一实体返回的实例的唯一性。这就是为什么你的eager load返回先前加载的延迟代理,如果实体的同一个会话中有一个(顺便说一下,那么应该完全初始化)。

根据您的应用程序的工作方式,您可能很难躲避这种情况。

您可以在急切加载之前Clear会话以避免这种情况,但是这将取消所有挂起的更改,并且当任何先前加载的实体与会话分离时,任何未加载的代理都将无法使用。

如果你事先有参考,你可以改为Evict你的实体,但在阅读你的问题时看起来并不是这样。

我宁愿调整映射层以支持获取代理或基本实体类。这个question与此有关,并且有很多有趣的答案。

在您的情况下,Diego Mijelshon answer's linked blog可以很好地让您的地图图层始终获得具体的类。

它包括向您的实体类添加一个属性,它将为您提供具体的实例,无论您是否拥有它,或者您拥有代理。

public virtual object Actual { get { return this; } }

正如他警告的那样,这是一个黑客攻击。通过此方法获得的具体实例之后不应与NHibernate会话一起使用。