【问题标题】:Verify that NHibernate has lazy loaded a many-to-one property or not验证 NHibernate 是否延迟加载了多对一属性
【发布时间】:2011-02-01 22:58:28
【问题描述】:

我有一个项目和一个人(有些简化):

public class Project
{
    public virtual Person CreatedBy { get; set; }
}

public class Person
{
    public virtual string Username { get; set; }
}

我正在与 NH Linq 合作一个项目:

var projects = from p in session.Query<Project>()
             select p;
var project = projects.First();

有时使用 Fetch()

var project = projects.Fetch(p => p.CreatedBy).First();

现在,我如何在单元测试中验证 project.CreatedBy 不是 急切加载(反之亦然),具体取决于 Fetch() 是否用过的?据我了解,使用 Fetch() 应该预先加载 Person,如果我不使用 Fetch(),它应该延迟加载 Person。

我试过了

Assert.IsTrue(NHibernateUtil.IsInitialized(project.CreatedBy));

还有

NHibernateUtil.IsPropertyInitialized(project, "CreatedBy")

但无论我是否使用 Fetch(),它们都返回 true。 IsInitialized() 适用于集合(一对多),例如 Person.Projects,但不适用于多对一...

CreatedBy 属性应该是延迟加载的,但有时我希望它被急切加载,并且我希望通过单元测试确认它按预期工作。

CreatedBy 属性的映射是

<many-to-one name="CreatedBy" class="Person" fetch="select">
  <column name="PERSON_ID" precision="10" scale="0" not-null="true" />
</many-to-one>

我验证了这样的映射:

var to1 = NHibernateConfiguration.GetClassMapping(typeof(Project))
    .ReferenceablePropertyIterator.FirstOrDefault(p => p.Name == "CreatedBy")
    .Value as NHibernate.Mapping.ToOne;

Assert.IsTrue(to1.IsLazy);
Assert.IsFalse(to1.UnwrapProxy);

通过了。

有什么想法吗?

【问题讨论】:

    标签: nhibernate nhibernate-mapping lazy-loading


    【解决方案1】:

    如果NHibernateUtil.IsInitialized(project.CreatedBy)返回true,那是因为它被初始化了。

    如果 Person 已经在会话中加载,无论您是否急切地获取 CreatedBy,它都会被初始化,这要归功于 Identity Map。

    【讨论】:

    • 会不会 IsInitialized 返回 true,因为该特定属性已由 NHibernate 使用代理初始化?
    • 没有。在未初始化的代理上调用 IsInitialized 将返回 false,无论您是从属性、调用 session.Load 还是任何其他方式获取它。
    • 嗯,害怕那个...这是在测试中,所以所有项目都是在设置中创建的,所有对象都从会话中逐出,会话被刷新。那时可能错过了一些东西。明天会调查它。感谢您迄今为止的帮助。
    • 调用 Evict 不会“取消初始化”已初始化的代理。
    • 在测试中清理会话的好方法是什么,以便我的所有测试在会话中什么都没有运行?我认为 Session.Flush() / .Evict() 应该可以解决问题。
    【解决方案2】:

    我看到的一个解决方案如下:

    而不是使用自动属性, 对于 CreatedBy,使用“经典 属性',它使用一个支持 字段,像这样:

    private Person _createdBy;
    
    public Person CreatedBy
    {
        get { return _createdBy; }
        set { _createdBy = value; }
    }
    

    在您的映射文件中,指明 NHibernate 应该使用支持字段,而不是属性:

    <many-to-one name="CreatedBy" class="Person" access="field.camelcase-underscore" fetch="select">
      <column name="PERSON_ID" precision="10" scale="0" not-null="true" />
    </many-to-one>
    

    然后,您能做的最美妙的事情是在您的单元测试项目中创建一个扩展方法,该方法检查 _createdBy 字段是否已初始化。

    很遗憾,您无法在扩展方法中访问私有成员。

    因此,我认为您可以在 Project 类上创建一个内部方法,用于检查 _createdBy 字段是否已初始化。 您还应该使用[InternalsVisibleTo][1] 属性,以便包含您的单元测试的项目可以访问您的Project 类中的内部方法。

    public class Project
    {
         private Person _createdBy;
    
         public Person CreatedBy
         {
            get{return _createdBy;}
            set{ _createdBy = value; }
         }
    
         internal IsCreatedByInitialized()
         {
              return _createdBy != null;
         }
    }
    

    在您的单元测试中,您可以这样做:

    Assert.IsTrue (p.isCreatedByInitialized());
    

    请注意,如果您的单元测试在一个单独的项目中,您需要指出该程序集应该可以访问内部方法: 因此,在Project 类所在项目的AssemblyInfo.cs 文件中,您应该这样做:

    [InternalsVisibleTo ("MyTestProject")]
    

    【讨论】:

    • 我真的不想为了满足我测试的一小部分而重新设计我的整个域模型和映射。
    • 对此感到抱歉。您刚刚询问了人们的意见......并且“改造”域模型有点夸张。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    相关资源
    最近更新 更多