对于应用程序来说,能够对NHibernate内部发生的事件做出响应式很有用的。这能够有助于实现一些类的功能或者扩展NHibernate的功能。
拦截器
IInterceptor接口提供了应用程序session的的回调方法,使得应用程序能够在持久化对象进行增删改查之前检测和/或者操作持久化对象的属性。一个应用场景是追踪审计信息。例如,下面的IInterceptor接口会在IAuditable实例新增的时候自动设置createTimeStamp,在IAuditable实例更新的时候自动更新lastUpdateTimestamp。
你可以直接实现IInterceptor或者(推荐)扩展EmptyInterceptor。
using
System; using
NHibernate; using
NHibernate.Type; public class
AuditInterceptor : EmptyInterceptor { private int
updates; private int
creates; private int
loads; public override void OnDelete(object
entity, object
id, object
[] state, string
[] propertyNames, IType[] types) { // do nothing
} public override bool OnFlushDirty(object
entity, object
id, object
[] currentState, object
[] previousState, string
[] propertyNames, IType[] types) { if ( entity is
IAuditable ) { updates++
; for ( int i=0; i < propertyNames.Length; i++
) { if ( "lastUpdateTimestamp"
.Equals( propertyNames[i] ) ) { currentState[i] = new
DateTime(); return true
; } } } return false
; } public override bool OnLoad(object
entity, object
id, object
[] state, string
[] propertyNames, IType[] types) { if ( entity is
IAuditable ) { loads++
; } return false
; } public override bool OnSave(object
entity, object
id, object
[] state, string
[] propertyNames, IType[] types) { if ( entity is
IAuditable ) { creates++
; for ( int i=0; i<propertyNames.Length; i++
) { if ( "createTimestamp"
.Equals( propertyNames[i] ) ) { state[i] = new
DateTime(); return true
; } } } return false
; } public override void
AfterTransactionCompletion(ITransaction tx) { if
( tx.WasCommitted ) { System.Console.WriteLine("Creations: " + creates + ", Updates: " + updates, "Loads: " +
loads); } updates=0
; creates=0
; loads=0
;
}
}
拦截器有两种类型:ISession-scoped 和ISessionFactory-scoped。
一个ISession-scoped 类型的拦截器会在使用ISessionFactory.OpenSession() 重载方法打开一个session时候被指定传入。
一个ISessionFactory-scoped 类型的拦截器会在产生ISessionFactory之前的Configuration 对象中设置。在这种情况下,配置后的拦截器会应用到所有通过ISessionFactory打开的session中,除非这个session在打开的时候显式设置了要使用的拦截器。ISessionFactory-scoped类型的拦截器必须是线程安全的,要注意不能保存特定session的状态,因为多个session会并发使用(潜在的)这个拦截器。
事件系统
如果你必须在你的持久化层中相应特定的事件,你可能还需要使用NHibernate 2 event 架构。事件系统可以作为拦截器的扩展或方案者替代品。
实际上所有ISession中所有的方法都对应一个事件,LoadEvent, FlushEvent 等(查询XSD配置文件或者NHibernate.Event 名称空间来获得被定义的事件类型列表)。当一个请求由这些方法其中一个组成的时候,ISession产生一个相应的事件然后将它传到配置好的该类型的事件监听器中。这些待命的监听器就会执行和这些方法相同处理过程。然而,你也可以自己实现任意的监听器接口(例如,注册的ILoadEventListener接口实现将会处理 LoadEvent事件),在这种情况下他们的视线会用来处理ISession产生的所有load()方法请求。
监听器应该是单例,也就是说,它们被所有的请求共享,因此它们不能保存任何的状态信息。
自定义的监听器应该实现相应它要处理事件的接口和/或者扩展基类(或者甚至NHibernate已经实现的默认事件监听器,它们的实现为此而被定义成虚方法)。自定义的监听器可以编程式地通过Configuration 对象来注册,或者在NHibernate XML配置文件中指定。下面是一个自定义加载事件监听器的例子:
);
}
}
}
你也需要配置入口来告诉NHibernate除了默认监听器之外还应该使用哪些监听器。
...
<event type="load"> <listener class="MyLoadListener"/> <listener class="NHibernate.Event.Default.DefaultLoadEventListener"/> </event> </session-factory> </hibernate-configuration>
你也可以在程序中来指定:
声明式地指定的监听器不能够共享实例。如果相同的类名在多个<listener/>标签中使用,每个引用都会导致一个独立的该类的实例。如果你需要在两个监听器类型之间共享监听器实例,你不惜使用这种编程式的注册方式。
为什么要在配置的时候实现一个借口和指定特定的类型呢?嗯,一个监听器的实现可以时间多个事件监听接口。在注册的时候额外地定义这些类是为了能够在配置的时候灵活的关闭或打开这些自定义的监听器。