Extra lazy collections

Sometimes we only need to know the number of items in a collection. It may be for displaying purposes or some behavioral logic. Either way, it seems a bit resource intensive that we should have to load all of the entities from the database, just to count them. With extra lazy collections we can improve this situation a bit.

Getting ready

Refer to the Getting Ready section of this chapter instructions at the beginning of this chapter.

How to do it…

  1. Add a new folder named ExtraLazy to the project.
  2. Add a new class named Accessory to the folder, having the code:
    using NH4CookbookHelpers.Queries.Model;
    
    namespace QueryRecipes.ExtraLazy
    {
        public class Accessory : Entity
        {
            public virtual string Name { get; set; }
        }
    }
  3. Add a new class named Car to the folder:
    using System;
    using System.Collections.Generic;
    
    namespace QueryRecipes.ExtraLazy
    {
        public class Car 
        {
            public Car()
            {
                Accessories=new HashSet<Accessory>();
            }
    
            public virtual Guid Id { get; protected set; }
            public virtual string Make { get; set; }
            public virtual string Model { get; set; }
            public virtual ISet<Accessory> Accessories { get; set; }
        }
    }
  4. Add a new embedded resource named Car.hbm.xml to the folder:
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="QueryRecipes"
        namespace="QueryRecipes.ExtraLazy">
      <class name="Car">
        <id name="Id">
          <generator class="guid.comb" />
        </id>
        <property name="Make" />
        <property name="Model" />
        <set name="Accessories" table="CarAccessories" lazy="extra" cascade="all">
          <key column="CarId" foreign-key=""/>
          <composite-element class="Accessory">
            <property name="Name"/>
          </composite-element>
        </set>
      </class>
    </hibernate-mapping>
  5. Add a new class named Recipe to the folder, having the code:
    using System;
    using NH4CookbookHelpers.Queries;
    using NHibernate;
    using NHibernate.Cfg;
    
    namespace QueryRecipes.ExtraLazy
    {
        public class Recipe : QueryRecipe
        {
            private Guid _carId;private int _firstAccessoryId;
    
            protected override void Configure(
              Configuration nhConfig)
            {
                nhConfig.AddResource(
    "QueryRecipes.ExtraLazy.Car.hbm.xml", GetType().Assembly);
            }
        }
    }
  6. Add a new method to the class, to add some data:
    protected override void AddData(
      ISessionFactory sessionFactory)
    {
      using (var session = sessionFactory.OpenSession())
      {
        using (var tx = session.BeginTransaction())
        {
          var car = new Car { Make = "SAAB", Model = "9-5" };
          for (var i = 0; i < 100; i++)
          {
            var accessory = new Accessory { 
              Name = "Accessory" + i };
            car.Accessories.Add(accessory);
          }
          session.Save(car);
          _carId = car.Id;
          _firstAccessoryId = car.Accessories.First().Id;
          tx.Commit();
        }
      }
    }
  7. Add a new method to the class, to test the configuration:
    protected override void Run(ISession session)
    {
      //Get the car
      var car = session.Get<Car>(_carId);
      //And one of the accessories
      var accessory = 
        session.Get<Accessory>(_firstAccessoryId);
      Console.WriteLine("Accessory count: {0}", 
        car.Accessories.Count);
      Console.WriteLine("Car has accessory {0}: {1}", 
        accessory.Name, car.Accessories.Contains(accessory));
    }
  8. Run the application and start the ExtraLazy recipe.

How it works…

In this mapping, the special addition is that we have specified the accessories collections laziness to be extra. This means that the accessory data isn't loaded from the database unless it's really, really needed. Just asking for the number of accessories on the car with car.Accessories.Count is not such an occasion. All that's really needed is to execute a COUNT query, and that's exactly what happens. The query log shows:

SELECT count(AccessoryId) 
FROM CarAccessories 
WHERE CarId=1

The next query is triggered by the Contains method, which can also be easily translated to a SQL query:

SELECT 1 FROM
CarAccessories 
WHERE CarId=1
AND AccessoryId=1

There's more…

The extra lazy collections added functionality is limited to Count and Contains. Any other call to the collection, such as Accessories.Any() will trigger a full load of the related data.

So, shouldn't we then always use lazy="extra", since it seems to be fool proof? No, the risk is that we need both the Count and all the items. If Count is executed before enumerating the data, we will have executed the COUNT query unnecessarily. Extra laziness is a convenient feature in certain scenarios, but it's usually better to code more explicit functions. If you only need to count, create a query that does just that.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.123.106