NHibernate功能的强大,勿庸置疑。但是很多地方使用起来也颇为繁琐,比如,无法完整的构建数据层、我们很多时候还是需要绕过NHibernate而直接使用ADO.net;比如,任何一次数据访问,哪怕只是简单的数据读取,我们都需要仔细的处理Session、Transaction。如果你的数据层使用NHibernate构建,那么类似下面的代码可能遍地开花:
ITransaction trans = null ;
ISession session = null ;
try
{
session = this.factory.OpenSession();
trans = session.BeginTransaction();
//访问数据库
trans.Commit();
}
catch (Exception ee)
{
if(trans != null)
{
trans.Rollback();
}
throw ee ;
}
finally
{
session.Close() ;
}
ISession session = null ;
try
{
session = this.factory.OpenSession();
trans = session.BeginTransaction();
//访问数据库
trans.Commit();
}
catch (Exception ee)
{
if(trans != null)
{
trans.Rollback();
}
throw ee ;
}
finally
{
session.Close() ;
}
这种try...catch...finally会在每个需要数据访问的地方重复,这种无聊的Repeat使我忍不住要做点什么。最初,我希望能达到using块的效果,比如像这样:
using(ISession session = this.factory.OpenSession())
{
//数据访问
}
{
//数据访问
}
在using块退出的时候,能自动关闭session和提交/回滚Transaction。如果能这样,那真是太完美了!可是,我真的还不知如何去实现它。(如果你知道,一定请告诉我)
于是我只好退而求其次,将上面代码中的注释掉的数据访问代码通过delegate抽取出来,这样那个try...catch...finally块就可以重用了。所以我首先定义了两个delegate,一个没有返回值的delegate用于处理“数据命令”,一个有返回值的delegate用于处理“数据查询”。
public delegate void CbDoSession(ISession session) ;
public delegate object CbDoSessionReturn(ISession session) ;
public delegate object CbDoSessionReturn(ISession session) ;
在此基础上,SessionQuicker就呼之欲出了:
public class SessionQuicker
{
private ISessionFactory factory ;
#region DataAssemblyName
private string dataAssemblyName = "ApplicationServerSystem" ;
public string DataAssemblyName
{
set
{
this.dataAssemblyName = value ;
}
}
#endregion
public void Initialize()
{
Configuration config = new Configuration().AddAssembly(this.dataAssemblyName);
this.factory = config.BuildSessionFactory();
}
#region ActionExceptionHandler
private IExceptionHandler actionExceptionHandler = null ;
public IExceptionHandler ActionExceptionHandler
{
set
{
this.actionExceptionHandler = value ;
}
}
#endregion
#region Action
public void Action(CbDoSession target ,params object[] args)
{
ITransaction trans = null ;
ISession session = null ;
try
{
session = this.factory.OpenSession();
trans = session.BeginTransaction();
target(session ,args) ;
trans.Commit();
}
catch (Exception ee)
{
if(trans != null)
{
trans.Rollback();
}
throw ee ;
}
finally
{
session.Close() ;
}
}
#endregion
public object ActionReturnObj(CbDoSessionReturn target ,params object[] args)
{
ITransaction trans = null ;
ISession session = null ;
try
{
session = this.factory.OpenSession();
trans = session.BeginTransaction();
object res = target(session ,args) ;
trans.Commit();
return res ;
}
catch (Exception ee)
{
if(trans != null)
{
trans.Rollback();
}
throw ee ;
}
finally
{
session.Close() ;
}
}
#region safty
public void ActionSafty(CbDoSession target ,params object[] args)
{
try
{
this.Action(target ,args) ;
}
catch(Exception ee)
{
if(this.actionExceptionHandler != null)
{
this.actionExceptionHandler.HanleException(ee) ;
}
}
}
public object ActionReturnObjSafty(CbDoSessionReturn target ,params object[] args)
{
try
{
return this.ActionReturnObj(target ,args) ;
}
catch(Exception ee)
{
if(this.actionExceptionHandler != null)
{
this.actionExceptionHandler.HanleException(ee) ;
}
return null ;
}
}
#endregion
}
public delegate void CbDoSession(ISession session ,params object[] args) ;
public delegate object CbDoSessionReturn(ISession session ,params object[] args) ;
{
private ISessionFactory factory ;
#region DataAssemblyName
private string dataAssemblyName = "ApplicationServerSystem" ;
public string DataAssemblyName
{
set
{
this.dataAssemblyName = value ;
}
}
#endregion
public void Initialize()
{
Configuration config = new Configuration().AddAssembly(this.dataAssemblyName);
this.factory = config.BuildSessionFactory();
}
#region ActionExceptionHandler
private IExceptionHandler actionExceptionHandler = null ;
public IExceptionHandler ActionExceptionHandler
{
set
{
this.actionExceptionHandler = value ;
}
}
#endregion
#region Action
public void Action(CbDoSession target ,params object[] args)
{
ITransaction trans = null ;
ISession session = null ;
try
{
session = this.factory.OpenSession();
trans = session.BeginTransaction();
target(session ,args) ;
trans.Commit();
}
catch (Exception ee)
{
if(trans != null)
{
trans.Rollback();
}
throw ee ;
}
finally
{
session.Close() ;
}
}
#endregion
public object ActionReturnObj(CbDoSessionReturn target ,params object[] args)
{
ITransaction trans = null ;
ISession session = null ;
try
{
session = this.factory.OpenSession();
trans = session.BeginTransaction();
object res = target(session ,args) ;
trans.Commit();
return res ;
}
catch (Exception ee)
{
if(trans != null)
{
trans.Rollback();
}
throw ee ;
}
finally
{
session.Close() ;
}
}
#region safty
public void ActionSafty(CbDoSession target ,params object[] args)
{
try
{
this.Action(target ,args) ;
}
catch(Exception ee)
{
if(this.actionExceptionHandler != null)
{
this.actionExceptionHandler.HanleException(ee) ;
}
}
}
public object ActionReturnObjSafty(CbDoSessionReturn target ,params object[] args)
{
try
{
return this.ActionReturnObj(target ,args) ;
}
catch(Exception ee)
{
if(this.actionExceptionHandler != null)
{
this.actionExceptionHandler.HanleException(ee) ;
}
return null ;
}
}
#endregion
}
public delegate void CbDoSession(ISession session ,params object[] args) ;
public delegate object CbDoSessionReturn(ISession session ,params object[] args) ;
在SessionQuicker的帮助下,你可以更简单的进行数据访问,比如:
private void OnButton1_Click(object sender ,EventArgs args)
{
IList list = (IList)sessionQuicker.ActionReturnObj(new CbDoSessionReturn(this.ReadStudentList)) ;
//处理list
}
private object ReadStudentList(ISession ss)
{
IQuery query = ss.CreateQuery("from Student as stu where stu.Age > :studentAge" ) ;
query.SetInt32("studentAge" ,20) ;
return query.List() ;
}
{
IList list = (IList)sessionQuicker.ActionReturnObj(new CbDoSessionReturn(this.ReadStudentList)) ;
//处理list
}
private object ReadStudentList(ISession ss)
{
IQuery query = ss.CreateQuery("from Student as stu where stu.Age > :studentAge" ) ;
query.SetInt32("studentAge" ,20) ;
return query.List() ;
}
SessionQuicker的好处是避免了每次数据访问写重复的try...catch...finally块,但是与前面理想的using块比起来,优雅性就要差许多。
我期待着有人提出更优雅的解决方案!