Using read-only entities

By treating entities as read-only, we allow NHibernate to skip the memory and resource intensive dirty checking, which determines how and if an entity should be updated in the database. In Chapter 2, Models and Mappings, we learned how to configure the read-only behavior in a mapping. Here we'll see how the same thing can be accomplished programmatically, at runtime.

Getting ready

Complete the Getting Ready instructions at the beginning of Chapter 4, Queries.

How to do it...

  1. Add a new folder named ReadOnly to the QueryRecipes project.
  2. Create a class named Recipe in the folder:
    using NH4CookbookHelpers.Queries;
    using NH4CookbookHelpers.Queries.Model;
    using NHibernate;
    
    namespace QueryRecipes.ReadOnly
    {
      public class Recipe : QueryRecipe
      {
        private bool _readOnly=true;
    
        protected override void Run(ISessionFactory sessionFactory)
        {
          RunWithReadOnlySession(sessionFactory);
          RunWithQuery(sessionFactory);
          RunWithSetReadOnly(sessionFactory);
        }
    
        private void RunWithReadOnlySession(ISessionFactory sessionFactory)
        {
          using (var session = sessionFactory.OpenSession())
          {
            session.DefaultReadOnly = _readOnly;
            using (var tx = session.BeginTransaction())
            {
              var movie = session.Get<Movie>(1);
              movie.Director = "Updated in session";
              tx.Commit();
            }
          }
        }
        
        private void RunWithQuery(ISessionFactory sessionFactory)
        {
          using (var session = sessionFactory.OpenSession())
          {
            using (var tx = session.BeginTransaction())
            {
              var query = session.QueryOver<Movie>()
                .Where(x => x.Id == 1);
               
              if (_readOnly)
              {
                query.ReadOnly();
              }
              var movie=query.SingleOrDefault();
    
              movie.Director = "Updated in query";
              tx.Commit();
            }
          }
        }
    
        private void RunWithSetReadOnly(ISessionFactory sessionFactory)
        {
          using (var session = sessionFactory.OpenSession())
          {
            using (var tx = session.BeginTransaction())
            {
              var movie = session.Get<Movie>(1);
              session.SetReadOnly(movie, _readOnly);
              movie.Director = "Updated with SetReadOnly";
              tx.Commit();
            }
          }
        }
      }
    }
  3. Run the application and start the ReadOnly recipe.

Note

How the movie is never updated, despite being modified several times. Set the _readOnly variable to false and run the recipe again. This time the entity will be updated thrice.

How it works...

We have already covered how to set a specific class as read-only in the mapping. In this recipe, we used three different techniques to configure the same thing at runtime.

Setting the session to be read-only

In the RunWithReadOnlySession method, we configure the session using the DefaultReadOnly property. This means that any entity that is loaded from the database into the session, after the property and set to true, will be marked as read-only. Toggling the value will never affect any entities already loaded.

Setting a query to load entities as read-only

We can also configure a specific query to load the entities with the read-only flag set. For an HQL query or a native SQL query, in other words a query implementing IQuery, this is done using the SetReadOnly(bool) method. The same method is also available on CriteriaQueries.

SetReadOnly will only affect the entities that are directly loaded by the query. Lazy loaded entities are not affected, but will respect the session's DefaultReadOnly setting. It's therefore sometimes better to just set DefaultReadOnly to true before a query is run and reset it to false once all the entities have been loaded.

It's also worth noting that specifying SetReadOnly on a query will override the DefaultReadOnly setting, making it possible to force query loaded entities to be writable (non read-only) even if DefaultReadOnly is true.

Our recipe uses the QueryOver syntax, which instead of SetReadOnly has a ReadOnly method, which can only set the value to true. Currently, the LINQ provider doesn't support ReadOnly, although such a feature is scheduled for a future release.

Making a specific entity read-only

The most granular way to use read-only entities is to specify it on a specific entity. By calling session.SetReadOnly(entity, bool), we can cause a writable entity to become read-only or a read-only entity to become writable. In the latter case, NHibernate consider the current state of the entity to be the baseline. Any changes performed before setting the entity the writable will be ignored in the updates.

There's more...

A read-only entity may not be quite as read-only as expected. For further details, read the How it works… section in Chapter 2, Models and Mappings.

One more way to avoid dirty checks and update is by setting session.FlushMode = FlushMode.Never. We tell NHibernate that we never want any changes flushed to the database. Saving a new entity, with a database generated POID, will still call the database, but other than that no updates, deletes, or inserts will be performed, unless session.Flush() is explicitly called.

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

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