1

I have a class Unit with a Dictionary of Parts. The mapping looks like this

<class name="Unit" table="Units">
<id name="Id">
  <generator class="native"/>
</id>
<property name="CreationDate" />

<property name="MacAddress" />
<property name="OEMId" />
<property name="SerialNumber" />
<property name="QualityControlState" />

<map name="Parts" table="UnitParts" lazy="false">
  <key column="UnitId"/>
  <index column="TypeOfPart" type="integer"/>
  <composite-element class="UnitPart">
    <property name="PartInfo"/>
    <property name="Remarks"/>
  </composite-element>
</map>

When I Session.SaveOrUpdate(Unit) all goes well: the two tables are filled with the right data. And the unit is retreivable (with all its Parts) using Session.Get(typeof(Unit) as well.

The problem: Obtaining multiple units lazy loads the Parts Dictionary.

The problem rephrased in pseudo code: LoadMethodOfPartsDictionary = (Stepping with Debugger) ? Eagerly : Lazy;

The following code baffles me. When I step through it using the debugger the Parts Dictionary of a Unit is eagerly loaded. But when I run to return Units (not stepping through Units = crit.Future<Unit>().ToList<Unit>();) its seems NHibernate wants to lazy load as the Parts Dictionary suddenly is NHibernate.Collection.Generic.PersistentGenericMap.

/// <summary>
/// Retreives a list of units by a set of criteria.
/// </summary>
/// <returns>A list of units that fall within/match the criteria</returns>
public static List<Unit> GetUnits(List<KeyValuePair<Unit.SortableProperties, ListSortDirection>> SortColumnOrder, out uint NumberOfRecordsWithoutLimit, uint Start = 0, int End = -1, FilterUnits Filter = default(FilterUnits))
{
    List<Unit> Units = default(List<Unit>);

    NumberOfRecordsWithoutLimit = 0;
    using (ISession Session = ORM.SessionFactory.OpenSession())
    {
        using (ITransaction Transaction = Session.BeginTransaction())
        {
            ICriteria crit = Session.CreateCriteria<Unit>();

            //Limit result set, used for paging
            if (End > 0)
            {
                crit.SetFirstResult((int)Start);
                crit.SetMaxResults(End);
            }

            //TODO: Implement filter code here

            //Add the sort order
            foreach (KeyValuePair<Unit.SortableProperties, ListSortDirection> kvp in SortColumnOrder)
            {
                String PropertyName = "";
                switch (kvp.Key)
                {
                    case Unit.SortableProperties.PRODUCTIONDATE:
                        PropertyName = "CreationDate";
                        break;
                    default:
                        throw new NotImplementedException(kvp.Key.ToString() + " isn't implemented for sorting yet.");
                }
                crit.AddOrder(new Order(PropertyName, (kvp.Value == ListSortDirection.Ascending)));
            }

            if (End > 0)
            {
                //Count the total units available in database.
                Units = crit.Future<Unit>().ToList<Unit>(); //This seems to lazy load the Units
                IFutureValue<int> RowCount = Session.CreateCriteria<Unit>()
                                        .SetProjection(Projections.Count(Projections.Id()))
                                        .FutureValue<int>();
                NumberOfRecordsWithoutLimit = (uint)RowCount.Value;
            }
            else
            {
                Units = (List<Unit>)crit.List<Unit>();
                NumberOfRecordsWithoutLimit = (uint)Units.Count;
            }

            Transaction.Commit();
            Session.Close();
            return Units;
        }
    }
}

Any hints are appreciated.

P.S. I used the [Debugging] tag as this seems to be key in this scenario.

4

1 回答 1

2

如何从数据库接收数据有两个不同的概念。如果是一对多(列表、字典)。

1)我们可以调用NHibernate通过ID或者引用获取数据(类似情况)

session.Get<Unit>(1);

在这种情况下,NHibernates 会注入所有标记为惰性的属性。因此,在您的情况下,这也会导致急切地加载零件。

类似的是参考属性:public virtual Unit Unit { get; set; }aneOtherObject

var otherObject = ... // get from session
var unit = otherObject.Unit; // the full Unit with all `Lazy="false"` properties is loaded

2)但我们也可以使用Criteria(如您的情况)。但是,这是不同的方法。在这种情况下,我们明确表示我们只想与Unit

Units = crit.Future<Unit>().ToList<Unit>();

这有很多优点:

  • 我们只能在Unit桌子上进行有效的分页
  • 仅加载单元(可能不会使用零件)
  • 如果使用了部件,它们将被延迟加载

但是如果真的需要(考虑到诸如错误分页之类的副作用),我们也可以在一个选择中加载 Parts:

Units = crit
  .SetFetchMode("Parts", FetchMode.Eager)
  .Future<Unit>()
  .ToList<Unit>();

现在立即全部填充。

3)我会投票的方法(我使用 100%)是让收集变得懒惰,并分配batch-size 19.1.5。使用批量获取

扩展

向您展示如何更改映射:

<map name="Parts" table="UnitParts" lazy="true" batch-size="25">

从那一刻起,你的 Util 就是轻量级的。在真正需要之前,不加载任何部件。如果在整个请求过程中打开会话,一旦触摸到 Parts,它们将在一个 SELECT 中加载(如果 Utils 的页面大小不大于 25)

这种方法非常有效,因为它使用了 ORM 的力量——一切都是惰性的

于 2013-08-15T04:33:02.727 回答