【问题标题】:NHibernate narrowing proxy warningNHibernate 缩小代理警告
【发布时间】:2011-11-25 10:44:47
【问题描述】:

我们正在构建一个使用 NH 进行数据访问的 ASP.NET MVC 应用程序。使用 NH Profiler 我看到很多警告,例如“警告:缩小 Domain.CaseTask 的代理 - 此操作中断 ==”。在为每个子类映射到一个表中的类执行查询时,我经常遇到这些问题,例如,使用 NH Linq 提供程序:

Query<ICaseTask>().Where(c => c.Assignee == Of || c.Operator == Of)

CaseTask 类从 Task 继承,触发警告。

互联网上有关该警告的信息很少,并且大多暗示这是可以忽略的……此警告到底警告了什么?这应该是我应该寻求纠正的事情吗?

【问题讨论】:

    标签: nhibernate


    【解决方案1】:

    现实更加复杂。当您使用session.Load 加载实体或访问延迟加载的属性时,NHibernate 将返回一个代理对象。当您第一次访问它的任何属性时,该代理对象将被水合(数据将从数据库加载)。为了实现这一点,NHibernate 生成了 扩展 实体类并覆盖所有属性 getter 和 setter 的代理类。这在不使用继承时非常有效,因为您无法区分代理和实体类(代理基类),例如简单的测试proxy is MyEntity 将始终有效。

    现在假设我们有一个 Person 实体:

    class Person {
      // lazy-loaded
      public Animal Pet { get; set; }
    }
    

    我们还有Animal 类层次结构:

    public abstract class Animal { ... }
    public class Cat { ... }
    public class Dog { ... }
    

    现在假设 Pet 属性是延迟加载的,当你向 NHibernate 询问 person pet 时,你会得到一个代理对象:

    var pet = somePerson.Pet; // pet will be a proxy
    

    但是由于Pet 是延迟加载的属性,NH 将不知道它是Cat 还是Dog 的实例,所以它会尽力而为,并会创建一个扩展Animal 的代理。代理将通过pet is Animal 的测试,但将无法通过pet is Catpet is Dog 的测试。

    现在假设您将访问 pet 对象的某些属性,从而强制 NH 从 DB 加载数据。现在 NH 会知道您的宠物是Cat 但代理已生成且无法更改。 这将强制 NHibernate 发出警告,即扩展类型 Animalpet 的原始代理将缩小为类型 Cat。这意味着从现在开始您使用session.Load&lt;Animal&gt;(pet.Id) 创建的具有pet.Id 的动物的代理对象将从现在扩展Cat。这也意味着,由于 Cat 现在作为会话的一部分存储,如果我们加载与第一个共享 cat 的第二个人,NH 将使用已经可用的 Cat 代理实例来填充延迟加载的属性。

    其中一个后果是对pet 的对象引用与session.Load&lt;Animal&gt;(pet.Id) 获得的引用不同(在object.ReferencesEqual 意义上)。

    // example - say parent and child share *the same* pet
    var pet = child.Pet; // NH will return proxy that extends Animal
    pet.DoStuff(); // NH loads data from DB
    
    var parent = child.Parent; // lazy-loaded property
    var pet2 = parent.Pet; // NH will return proxy that extends Cat
    
    Assert.NotSame(pet, pet2);
    

    现在这可能会对您造成伤害:

    1. 当您将实体放入代码中的Sets 或Dictionaryies 时,或者如果您使用任何其他需要Equals/GetHashCode 对才能工作的结构。这可以通过提供自定义 Equals/GetHashCode 实现轻松解决(请参阅:http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=1

    2. 当您尝试将代理对象转换为目标类型时,例如(Cat)pet,但同样有已知的解决方案(例如Getting proxies of the correct type in NHibernate

    因此,道德是尽可能避免在您的域模型中继承。

    【讨论】:

    • LOL 在你的领域模型中尽可能避免继承是道德的吗?似乎那里一定有比这更好的道德......
    【解决方案2】:

    此警告是关于具有作为子类的属性或字段的类。即:

    public class Animal
    {
        public int Id {get;set;}
    }
    
    public class Cat : Animal
    {
        public int Weight {get;set;}
    }
    
    public class Person
    {
        public Cat Pet {get;set;}
    }
    

    NHibernate 在加载人员实体时会感到不安,因为它不想为您强制转换,因为行为变得不可预测。除非您告诉 NHibernate 如何处理 Equals(以及其他逻辑),否则它不会知道如何自行进行比较。

    纠正这个问题的基本思路是让 NHibernate 将基类对象放入图中,然后您处理转换(请注意,此设置将使用一些略有不同的映射 - 我这样做是为了简化代码,但它可以显然可以通过将属性保持为完整的 getter/setter 来完成):

    public class Animal
        {
            public int Id {get;set;}
        }
    
    public class Cat : Animal
    {
        public int Weight {get;set;}
    }
    
    public class Person
    {
        private Animal _pet;
        public Cat Pet {
            get{return _pet as Cat;}
        }
    }
    

    【讨论】:

    • 这很重要吗?如果我们忽略这个 WARN 会发生什么?
    • 这是否重要取决于您愿意接受的风险水平。由于您的代码和数据库之间总是会断开连接,因此您不能始终确保强制转换能够正常工作。这将导致可能难以诊断的错误,并且如果不更改数据库或代码可能无法解决。
    猜你喜欢
    • 1970-01-01
    • 2014-01-29
    • 1970-01-01
    • 1970-01-01
    • 2013-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多