Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

May 2011 - Posts

NHibernate Pitfalls: Lazy Properties in Non-Lazy Entities

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

If you want to have lazy properties on your entity, your entities must also be lazy. This applies to basic type or array type properties (typically String or Byte[] mapped to CLOB or BLOB columns) as well as entity type references (one to one, many to one). This is because NHibernate will only create proxies (in practice, subclasses created at runtime that override the lazy properties) for entities marked as lazy. These properties, of course, must be declared as virtual, and need to be public, protected or protected internal, private or internal wont work, and the classes cannot be sealed.

A different matter is lazy collections (one to many): for these, classes don’t need to be inherited from as NHibernate will change the collection properties for their own collection classes that already implements the lazy behavior. These collection properties must though be declared as interfaces (IEnumerable<T>, ICollection<T>, IList<T>, Iesi.Collections.Generic.ISet<T>), not actual types, so that they can be assigned to NHibernate classes, which also implement these interfaces.

Bookmark and Share

NHibernate Pitfalls: Bags and Join

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

If you have an entity with a non-lazy loaded bag collection that I fetched using a join, you will get an incorrect number of items when you query a list of this entity: NHibernate will return as many elements as contained by the child table referenced by the bag, plus one for every item in the master table that does not have any child. This happens because NHibernate misinterprets the returned record set.

There are two solutions: either switch to a set, or apply a DistinctRootTransformer:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <hibernate-mapping default-lazy="false" namespace="SomeNamespace" assembly="SomeAssembly" xmlns="urn:nhibernate-mapping-2.2">
   3:     <class name="Order" lazy="false" table="`ORDER`">
   4:         <id name="OrderId" access="property" column="`ORDER_ID`">
   5:             <generator class="hilo" />
   6:         </id>
   7:         <property name="Customer" type="String" column="`CUSTOMER`" length="50" />
   8:         <set cascade="all-delete-orphan" inverse="true" lazy="false" fetch="join" name="Details">
   9:             <key column="`ORDER_ID`" />
  10:             <one-to-many class="OrderDetail" />
  11:         </set>
  12:     </class>
  13: </hibernate-mapping>
   1: //LINQ: no need for the result transformer
   2: var orders3 = session.Query<Order>().Fetch(o => o.Details).ToList();
   3: //HQL
   4: var orders1 = session.CreateQuery("from Order o join fetch o.Details").SetResultTransformer(Transformers.DistinctRootEntity).List();
   5: //Criteria
   6: var orders2 = session.CreateCriteria<Order>().SetResultTransformer(Transformers.DistinctRootEntity).List();

Interestingly, if you use LINQ you don’t have this problem.

Bookmark and Share

NHibernate Pitfalls: Many to Many and Inverse

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

When you have a many to many relation in NHibernate, you can only set inverse to false on one side of the relation, not on both:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <hibernate-mapping default-lazy="false" namespace="SomeNamespace" assembly="SomeAssembly" xmlns="urn:nhibernate-mapping-2.2">
   3:     <class name="User" lazy="false" table="`USER`">
   4:         <id name="UsrId" access="property" column="`USR_ID`">
   5:             <generator class="hilo" />
   6:         </id>
   7:         <set cascade="all-delete-orphan" inverse="true" lazy="true" name="Notifications" table="`NOT_USR`">
   8:             <key column="`USR_ID`" />
   9:             <many-to-many class="Notification" column="`NOT_ID`" />
  10:         </set>
  11:     </class>
  12: </hibernate-mapping>

And:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <hibernate-mapping default-lazy="false" namespace="SomeNamespace" assembly="SomeAssembly" xmlns="urn:nhibernate-mapping-2.2">
   3:     <class name="Notification" lazy="false" table="`NOTIFICATION`">
   4:         <id name="NotificationId" access="property" column="`NOT_ID`">
   5:             <generator class="hilo" />
   6:         </id>
   7:         <property name="Comment" column="`COMMENT`" type="String" length="50"/>
   8:         <set cascade="all-delete-orphan" lazy="true" name="Users" table="`NOT_USR`">
   9:             <key column="`NOT_ID`" />
  10:             <many-to-many class="User" column="`USR_ID`" />
  11:         </set>
  12:     </class>
  13: </hibernate-mapping>

If you don’t, you won’t be able to save any associations.

Bookmark and Share

NHibernate Pitfalls: Specifying Event Listeners in XML Configuration

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

If you specify an event listener in XML configuration (App.config, Web.config, hibernate.cfg.xml) you are replacing the NHibernate standard event listener, not adding your own:

   1: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
   2:         <bytecode-provider type="lcg"/>
   3:         <reflection-optimizer use="true"/>
   4:         <session-factory>
   5:             <property name="adonet.batch_size">200</property>
   6:             <property name="adonet.wrap_result_sets">false</property>
   7:             <property name="cache.use_query_cache">true</property>
   8:             <property name="command_timeout">20</property>
   9:             <property name="connection.connection_string_name">NHibernate</property>
  10:             <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
  11:             <property name="connection.isolation">ReadCommitted</property>
  12:             <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
  13:             <property name="format_sql">true</property>
  14:             <property name="generate_statistics">false</property>
  15:             <property name="hbm2ddl.keywords">none</property>
  16:             <property name="query.startup_check">false</property>
  17:             <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
  18:             <property name="show_sql">true</property>
  19:             <mapping assembly="FlushTest"/>
  20:             <listener type="flush-entity" class="FlushEntityEventListener, MyAssembly"/>
  21:         </session-factory>
  22:     </hibernate-configuration>

If you want to do it by configuration file, make sure you also add NHibernate’s, but you should instead be doing it by code:

   1:             Configuration cfg = new Configuration().Configure();
   2:             cfg.EventListeners.FlushEntityEventListeners = cfg.EventListeners.FlushEntityEventListeners.Concat(new[] { new FlushEntityEventListener() }).ToArray();
Bookmark and Share
Loading Entities from the First Level Cache

You can use the following code to load all entities of a given type from the first level cache (the entities that were loaded from the current session):

   1: //load all entities
   2: public static IEnumerable<T> Local<T>(this ISession session)
   3: {
   4:     ISessionImplementor impl = session.GetSessionImplementation();
   5:     IPersistenceContext ctx = impl.PersistenceContext;
   6:  
   7:     return (ctx.EntityEntries.Keys.OfType<T>());
   8: }

Or a single entity by its id:

   1: //load a single entity by its id
   2: public static T GetFromCache<T>(this ISession session, Object id)
   3: {
   4:     ISessionImplementor impl = session.GetSessionImplementation();
   5:     IPersistenceContext ctx = impl.PersistenceContext;
   6:  
   7:     foreach (DictionaryEntry entry in ctx.EntityEntries)
   8:     {
   9:         if (entry.Key is T)
  10:         {
  11:             if (Object.Equals((entry.Value as EntityEntry).Id, id) == true)
  12:             {
  13:                 return ((T) entry.Key);
  14:             }
  15:         }
  16:     }
  17:  
  18:     return (default(T));
  19: }
Bookmark and Share
NHibernate Pitfalls: Collection Restrictions

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

Beware using filters and where clause restrictions to filter your NHibernate collections fetched with join. There is a known problem (JIRA issue NH-1930) which can be described like this:

  • You have a collection (set, bag, list, etc) mapped with the join fetch mode (eagerly loaded):
   1: <set where="DELETED = 0" cascade="all" inverse="true" lazy="false" fetch="join" name="Details">
   2:   <key column="`ORDER_ID`" />
   3:   <one-to-many class="OrderDetail" />      
   4: </set>
  • You apply to it a where clause or a filter;
  • The restriction is applied to the generated SQL’s global where clause:
   1: SELECT a.ID, 
   2: a.SOMEFIELD, 
   3: b.ID, 
   4: b.FKID, 
   5: b.SOMEOTHERFIELD, 
   6: b.ISDELETED 
   7: FROM a 
   8: LEFT JOIN b 
   9: ON b.FKID = a.ID 
  10: WHERE b.ISDELETED = 0 

instead of the INNER JOIN ON clause:

   1: SELECT a.ID, 
   2: a.SOMEFIELD, 
   3: b.ID, 
   4: b.FKID, 
   5: b.SOMEOTHERFIELD, 
   6: b.ISDELETED 
   7: FROM a 
   8: LEFT JOIN b 
   9: ON b.FKID = a.ID AND b.ISDELETED = 0 
  • If the related table does not have rows that match the association, no records will be returned at all, not even for the main entity that you are querying!

In order to go around it, you can either turn to using select as the fetch mode, disable the restriction or specify it like "DELETED = 0 OR DELETED = NULL". Or, of course, wait for the issue to be fixed!

Bookmark and Share

NHibernate Pitfalls: The SaveOrUpdate Event

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

You may be familiar with the NHibernate SaveOrUpdate event. In fact, you may have already attempted to use it in order to “audit stamping” your entities, by setting properties like CreatedBy, CreatedAt, UpdatedBy, UpdatedAt, etc.

It happens that you shouldn’t do it, mostly because SaveOrUpdate may be raised even when the session is not dirty and, consequently, there are no dirty entities. It happens on some circumstances as part of the NHibernate session’s dirty check procedure.

Instead, you should be using FlushEntity event, but you must also be sure that the entity that is passed to the event handler is not changed, before, well, changing it with the aforementioned “audit” properties. You should do the check using the event properties FlushEntityEvent.Status and FlushEntityEvent.HasDirtyCollection.

Fabio Maulo, the NHibernate team leader, has a post on it on his blog: http://fabiomaulo.blogspot.com/2011/05/nhibernate-bizarre-audit.html.

Bookmark and Share
Service Locator – Dependency Resolver Bridge
   1: public sealed class ServiceLocatorDependencyResolver: IDependencyResolver, IServiceLocator
   2:     {
   3:         #region Public constructor
   4:         public ServiceLocatorDependencyResolver(IServiceLocator serviceLocator)
   5:         {
   6:             if (serviceLocator == null)
   7:             {
   8:                 throw (new ArgumentNullException("serviceLocator"));
   9:             }
  10:  
  11:             this.ServiceLocator = serviceLocator;
  12:         }
  13:         #endregion
  14:  
  15:         #region Public properties
  16:         public IServiceLocator ServiceLocator
  17:         {
  18:             get;
  19:             private set;
  20:         }
  21:         #endregion
  22:  
  23:         #region Public IDependencyResolver methods
  24:         public Object GetService(Type serviceType)
  25:         {
  26:             try
  27:             {
  28:                 return (this.ServiceLocator.GetService(serviceType));
  29:             }
  30:             catch
  31:             {
  32:                 return (null);
  33:             }
  34:         }
  35:  
  36:         public IEnumerable<Object> GetServices(Type serviceType)
  37:         {
  38:             try
  39:             {
  40:                 return (this.ServiceLocator.GetAllInstances(serviceType));
  41:             }
  42:             catch
  43:             {
  44:                 return (null);
  45:             }
  46:         }
  47:         #endregion
  48:  
  49:         #region Public IServiceLocator methods
  50:         public IEnumerable<TService> GetAllInstances<TService>()
  51:         {
  52:             return (this.ServiceLocator.GetAllInstances<TService>());
  53:         }
  54:  
  55:         public IEnumerable<Object> GetAllInstances(Type serviceType)
  56:         {
  57:             return (this.ServiceLocator.GetAllInstances(serviceType));
  58:         }
  59:  
  60:         public TService GetInstance<TService>(String key)
  61:         {
  62:             return (this.ServiceLocator.GetInstance<TService>(key));
  63:         }
  64:  
  65:         public TService GetInstance<TService>()
  66:         {
  67:             return (this.ServiceLocator.GetInstance<TService>());
  68:         }
  69:  
  70:         public Object GetInstance(Type serviceType, String key)
  71:         {
  72:             return (this.ServiceLocator.GetInstance(serviceType, key));
  73:         }
  74:  
  75:         public Object GetInstance(Type serviceType)
  76:         {
  77:             return (this.ServiceLocator.GetInstance(serviceType));
  78:         }
  79:         #endregion
  80:     }

Bookmark and Share

Adding Modules Dynamically to ASP.NET

As you may already know, ASP.NET 4.0 brought along with it a new mechanism to execute methods before the application start event (the Application_Start method): the PreApplicationStartMethodAttribute. This attribute, when applied to an assembly, indicates a method which is to be run before the application starts. If your web application references any assembly which has this attribute, it will run the method it declares, throwing an exception it if is not found.

Of course, you may like or dislike this new behavior, but it is here, and there’s no way to make it go away, so let’s try to make some use out of it.

As it happens, ASP.NET MVC 3 introduces the Microsoft.Web.Infrastructure assembly (normally placed in C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies) which has a static class called Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility with a single public method:  RegisterModule. This method can be used to add a module (IHttpModule) to the application dynamically, that is, without registering it on the Web.config file.

Here’s an example:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Web;
   6: using Microsoft.Web.Infrastructure.DynamicModuleHelper;
   7:  
   8: [assembly: PreApplicationStartMethod(typeof(AutoModuleLibrary.AutoModule), "Install")]
   9:  
  10: namespace AutoModuleLibrary
  11: {
  12:     public class AutoModule: IHttpModule
  13:     {
  14:         public static void Install()
  15:         {
  16:             DynamicModuleUtility.RegisterModule(typeof(AutoModule));
  17:         }
  18:  
  19:         public void Dispose()
  20:         {
  21:         }
  22:  
  23:         public void Init(HttpApplication context)
  24:         {
  25:         }
  26:     }
  27: }

If you are concerned about the security implications, you must know that the method declared in the PreApplicationStartMethodAttribute attribute has no access to the current application context: the HttpContext.Current property is null; however, the static properties on HttpRuntime and AppDomain.CurrentDomain are not.

Have a try!

Bookmark and Share
More Posts