May 2011 - Posts
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.
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.
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.
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();
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: }
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!
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.
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: }
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!
More Posts