NHibernate Step By Step(3)-数据操作回调方法(拦截器)
2009-08-02 16:25 Clingingboy 阅读(1114) 评论(2) 编辑 收藏 举报
1.ILifecycle
2.IValidatable
3.Interceptor
ILifecycle接口
在数据操作的时候,我们可能需要做些格外的处理,比如Save方法可能就需要一个OnSave方法
NH提供了ILifecycle接口,用于回调
该接口包含4个方法,当实体类实现该接口时,session调用这些方法时,将会先检测,以返回的LifecycleVeto枚举作判断,Veto则禁止通过,NoVeto则允许数据操作执行
如下实体类
public class EntityWithLifecycle : ILifecycle { public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual double Heigth { get; set; } public virtual double Width { get; set; } public EntityWithLifecycle() {} public EntityWithLifecycle(string name, double heigth, double width) { Name = name; Heigth = heigth; Width = width; } public virtual LifecycleVeto OnSave(ISession s) { return IsValid() ? LifecycleVeto.NoVeto : LifecycleVeto.Veto; } public virtual LifecycleVeto OnUpdate(ISession s) { return IsValid() ? LifecycleVeto.NoVeto : LifecycleVeto.Veto; } public virtual LifecycleVeto OnDelete(ISession s) { return IsValid() ? LifecycleVeto.NoVeto : LifecycleVeto.Veto; } public virtual void OnLoad(ISession s, object id) { // nothing to do } public virtual IList<string> GetBrokenRules() { IList<string> result = new List<string>(3); if (string.IsNullOrEmpty(Name) || Name.Trim().Length < 2) result.Add("The Name must have more than one char."); if (Heigth <= 0) result.Add("Heigth must be great than 0"); if (Width <= 0) result.Add("Width must be great than 0."); return result; } /// <summary> /// Validate the state of the object before persisting it. If a violation occurs, /// throw a <see cref="ValidationFailure" />. This method must not change the state of the object /// by side-effect. /// </summary> private bool IsValid() { IList<string> br = GetBrokenRules(); return br == null || br.Count == 0; } }
测试,将无法保存数据到数据库中
using (ISession s = OpenSession()) { s.Save(new EntityWithLifecycle()); s.Flush(); }
IValidatable接口
public interface IValidatable { /// <summary> /// Validate the state of the object before persisting it. If a violation occurs, /// throw a <see cref="ValidationFailure" />. This method must not change the state of the object /// by side-effect. /// </summary> void Validate(); }
用于验证数据的正确性,实体实现该接口,在保存或更新数据操作时,都会调用此方法,若验证不通过,则抛出ValidationFailure错误
public class Video: IValidatable { private int id; private string name; private double heigth; private double width; public Video() {} public Video(string name, double heigth, double width) { this.name = name; this.heigth = heigth; this.width = width; } public virtual int Id { get { return id; } set { id = value; } } public virtual string Name { get { return name; } set { name = value; } } public virtual double Heigth { get { return heigth; } set { heigth = value; } } public virtual double Width { get { return width; } set { width = value; } } #region IValidatable Members public virtual IList<string> GetBrokenRules() { IList<string> result = new List<string>(3); if (string.IsNullOrEmpty(Name) || Name.Trim().Length < 2) result.Add("The Name must have more than one char."); if (Heigth <= 0) result.Add("Heigth must be great than 0"); if (Width <= 0) result.Add("Width must be great than 0."); return result; } /// <summary> /// Validate the state of the object before persisting it. If a violation occurs, /// throw a <see cref="ValidationFailure" />. This method must not change the state of the object /// by side-effect. /// </summary> public virtual void Validate() { IList<string> br = GetBrokenRules(); if (br != null && br.Count > 0) throw new ValidationFailure(BrokenRulesFormat(typeof(Video), br)); } private static string BrokenRulesFormat(System.Type entity, IList<string> brokenRulesDescriptions) { if (entity == null) throw new ArgumentNullException("entity"); if (brokenRulesDescriptions == null) throw new ArgumentNullException("brokenRulesDescriptions"); StringBuilder sb = new StringBuilder(50 + brokenRulesDescriptions.Count * 50) .AppendLine(string.Format("Entity:{0}", entity)); foreach (string message in brokenRulesDescriptions) sb.AppendLine(message); return sb.ToString(); } #endregion }
测试,程序将跳入catch中
try { using (ISession s = OpenSession()) { s.Save(new Video()); s.Flush(); } Assert.Fail("Saved an invalid entity"); } catch(ValidationFailure) { // Ok }
以上两种方法需要个别实体全部实现,若数量多的话就麻烦了,于是出现了拦截器
拦截器
比如对每次操作写入日志时,可使用此方法
使用拦截器方法
1.在session被创建时
ISession session = sf.OpenSession( new AuditInterceptor() );
2.在Configuration中声明
new Configuration().SetInterceptor( new AuditInterceptor() );
使用拦截器可以拦截到的数据进行修改,所以使用拦截器也需要小心,做的不好,可能会破坏原有程序
简单示例
public class StatefulInterceptor : EmptyInterceptor { private ISession session; private readonly IList list = new ArrayList(); public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types) { if (!(entity is Log)) { list.Add(new Log("insert", (string) id, entity.GetType().FullName)); } return false; } public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, IType[] types) { if ( !(entity is Log) ) { list.Add( new Log( "update", (string) id, entity.GetType().FullName ) ); } return false; } public override void PostFlush(ICollection entities) { if (list.Count > 0) { foreach (object iter in list) { session.Persist(iter); } list.Clear(); session.Flush(); } } public override void SetSession(ISession sessionLocal) { session = sessionLocal; } public ISession Session { get { return session; } } }
测试
StatefulInterceptor statefulInterceptor = new StatefulInterceptor(); ISession s = OpenSession(statefulInterceptor); Assert.IsNotNull(statefulInterceptor.Session); ITransaction t = s.BeginTransaction(); User u = new User("Gavin", "nivag"); s.Persist(u); u.Password = "vagni"; t.Commit(); s.Close(); s = OpenSession(); t = s.BeginTransaction(); IList logs = s.CreateCriteria(typeof(Log)).List(); Assert.AreEqual(2, logs.Count); s.Delete(u); s.Delete("from Log"); t.Commit(); s.Close();