ASP.NET Mvc + NHibernate + Unity Application Block 示例程序
今天无意间看到了Unity Application Block (以下简称Unity),感觉很不错,用起来挺方便的说。于是一时兴起写了这个留言本的小示例。数据库操作采用的NHibernate,UI直接用的
ASP.NET Mvc了。项目基于VS2010 + SQLServer 2005 ,就一个表的增删改查操作很简单。目的是为了体验下Unity的Ioc功能。
国际惯例,先上个项目结构图
项目只是演示使用,未建立公共类和配置项抽象出来等工作,各个项目如下:
Guestbook.Biz 业务层,具体业务实现。这里只实现了增删改查
Guestbook.Dal 持久层,基于NHibernate的ORM映射。实现增删改查
Guestbook.IBiz 业务层接口,给UI引用实现解藕。UI层不直接引用具体业务类,实现面向接口
Guestbook.IDal 持久层接口,给Biz引用实现解藕。方便更换不同数据库持久类(PS;虽然NHibernate 已经可以支持多库,这里纯为了娱乐)
Guestbook.Model 持久实体,NHibernate的映射文件也放到这个类库里面了
Guestbook.Unity 简单封装了一下Unity,传入相应接口即可New相应的在Web.Config中配置的具体业务类
Guestbook.Web UI层,ASP.NET Mvc项目。控制器直接引用的业务接口IBiz和Model还有Unity其它全部不引用
采取从下往上依次说各层的东西。首先从数据库。。。。(略)---从持久层说起吧。持久层采用了NHibernate基本配置和映射配置就不说了(hkgoogle),首先在Guestbook.Dal中写了一个NHibernateHelper帮助类,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// NHibernate操作辅助类
/// </summary>
public class NHibernateHelper
{
private ISessionFactory _sessionFactory;
public NHibernateHelper()
{
_sessionFactory = GetSessionFactory();
}
private ISessionFactory GetSessionFactory()
{
Configuration cfg = new Configuration();
ISessionFactory sf =
cfg.Configure().BuildSessionFactory();
return sf;
}
public ISession GetSession()
{
ISession session = _sessionFactory.OpenSession();
return session;
}
}
以上就是创建个NHibernate的Session。
接下来是在Guestbook.IDal定义持久层相关的接口,首先是一个通用的增删改查接口,如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 数据执行类接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IDbObject<T>
{
void Save(T entity);
void Delete(T entity);
void Update(T entity);
T Load(int sysNo);
IList<T> GetList();
}
定义基本的增删改查,是个泛型接口,这样实现它的类可以接收所有实本类,实现通用的方法。
接下来在Guestbook.Dal 定义一个 DbObject<T>基类,实现IDbObject<T>的增删改查方法,并做具体的实现,像下面这样:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 数据执行基础泛型类
/// </summary>
/// <typeparam name="T"></typeparam>
public class DbObject<T>:IDbObject<T> where T:class
{
private ISession _session;
public DbObject()
{
NHibernateHelper nhbHelper = new NHibernateHelper();
_session = nhbHelper.GetSession();
}
#region IDbObject<T> 成员
public void Save(T entity)
{
_session.Save(entity);
_session.Flush();
}
public void Delete(T entity)
{
_session.Delete(entity);
_session.Flush();
}
public void Update(T entity)
{
_session.Update(entity);
_session.Flush();
}
public T Load(int sysNo)
{
return _session.Load<T>(sysNo);
}
public IList<T> GetList()
{
return _session.CreateCriteria<T>().AddOrder(NHibernate.Criterion.Order.Asc("SysNo")).List<T>();
}
#endregion
比如 Save(T entity) 方法,实现具体的_session.Save(entity);_session.Flush();方法。通过NHibernate操作数据库。接下来就可以具体的持久业务类继承这个操作基类了。下面以Guestbook 为原型做相关讲解。为了数据层解藕,先建接口IGuestbookDal如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 留言本数据操作接口
/// </summary>
public interface IGuestbookDal
{
void Save(GuestbookInfo entity);
void Delete(GuestbookInfo entity);
void Update(GuestbookInfo entity);
GuestbookInfo Load(int sysNo);
IList<GuestbookInfo> GetList();
}
然后在Guestbook.Dal中建一个具体持久类,这个类继承DbObject<GuestbookInfo>基类并实现IGuestbookDal接口,代码如下:
/// 留言本数据处理类
/// </summary>
public class GuestbookDal:DbObject<GuestbookInfo>,IGuestbookDal
{
}
具体操作已经在泛型基类DbObject中实现了,所以这个类不用再实现什么操作即可。再加比如UserDal;ProductDal都可以直接继承不用实现就可以了。大大减少了很多工作量(之前园子里有觉得应该取消泛型,这么好的东西给我留着吧。阿们)。这样持久层基本就完成了。接下来说说业务类。
首先,一切为了解藕,一切为了面向接口。主阿,饶恕我吧。先在Guestbook.IBiz中定义个业务接口IGuestbookBiz吧:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 留言本业务接口
/// </summary>
public interface IGuestbookBiz
{
void Save(GuestbookInfo entity);
void Delete(GuestbookInfo entity);
void Update(GuestbookInfo entity);
GuestbookInfo Load(int sysNo);
IList<GuestbookInfo> GetList();
}
这没啥说的了,再说大家都烦了。再紧跟着在Guestbook.Biz中建一个业务类GuestbookBiz 实现 以上接口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 留言本业务类
/// </summary>
public class GuestbookBiz:IGuestbookBiz
{
private IGuestbookDal _guestbookDal = UnityHelper.UnityToT<IGuestbookDal>("DalType");
#region IGuestbookBiz 成员
public void Save(GuestbookInfo entity)
{
_guestbookDal.Save(entity);
}
public void Delete(GuestbookInfo entity)
{
_guestbookDal.Delete(entity);
}
public void Update(GuestbookInfo entity)
{
_guestbookDal.Update(entity);
}
public GuestbookInfo Load(int sysNo)
{
return _guestbookDal.Load(sysNo);
}
public IList<GuestbookInfo> GetList()
{
return _guestbookDal.GetList();
}
#endregion
}
注意这句 private IGuestbookDal _guestbookDal = UnityHelper.UnityToT<IGuestbookDal>("DalType");,这里是业务层引用持久层,并不是直接New的,而是通过持久接口IGuestbookDal定义的,通过Unity加载进来的。这样就实现了相当成份的解藕了,不是由使用者去引用并实例被引用者,这个操作是由Ioc容器来做(这里是Unity)。这里用到了UnityHelper类了,它的写法如下(没有深入学习,只是基本操作):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
/// <summary>
/// 通过Unity实组装对象泛型方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public static T UnityToT<T>(string name)
{
IUnityContainer container = CreateContainer();
T model = container.Resolve<T>(name);
return model;
}
/// <summary>
/// 通过Unity实组装对象泛型方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public static T UnityToT<T>()
{
IUnityContainer container = CreateContainer();
T model = container.Resolve<T>();
return model;
}
/// <summary>
/// 通过配置创建UnityContainer
/// </summary>
/// <returns></returns>
public static IUnityContainer CreateContainer()
{
IUnityContainer container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection)
System.Configuration.ConfigurationManager.GetSection("unity");
section.Containers["Guestbook"].Configure(container);
return container;
}
}
CreateContainer就是创建UnityContainer,它是Unity的核心所在。通过它就可以进行动态组装了。这里可以用代码创建也可以通过配置文件。我选择配置文件。因为直接写代码里面侵入太强了,失去了很多灵活装配的意义。配置我直接写在了Web.config里面,也可以单独写配置文件。我们在后面讲一下配置文件。UnityToT(string name)是我写的一个泛型方法,通过name查找相应的配置项去组装并反回接口的对象,T是我们的接口。使用就如上面:private IGuestbookDal _guestbookDal = UnityHelper.UnityToT<IGuestbookDal>("DalType");,<IGuestbookDal>就是接口,我们在配置里面有指定。"DalType"就是配置的名称。下面我们看一下相关的配置项:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<containers>
<container name="Guestbook">
<types>
<type name="BizType" type="Guestbook.IBiz.IGuestbookBiz,Guestbook.IBiz" mapTo="Guestbook.Biz.GuestbookBiz,Guestbook.Biz"/>
<type name="DalType" type="Guestbook.IDal.IGuestbookDal,Guestbook.IDal" mapTo="Guestbook.Dal.GuestbookDal,Guestbook.dal"/>
</types>
</container>
</containers>
</unity>
type节就是我们的对应配置项,name是我们前面泛型方法要传入的name,type为接口,mapTo为具体的业务类。比如上面的IGuestbookDal持久层接口对应的相应的GuestbookDal持久类,这样配置后Unity就会帮我们根据接口装配(实例化)实现类,并反回相应的实例对象,我们就可以通过这个对象实现具体业务了,比如:_dao.Save(entity);。
这样我们的业务层也完成了,接下来就是UI层调用业务层就可以了。UI层我们一样通过Unity进行装配,UI层只引用实体类Guestbook.Model和Guestbook.IBiz业务层接口(当然用Unity要引用相应的UnityHelper类了)。这样UI层对业务层向下层次全部是弱依赖。UI我们基于的ASP.NET Mvc,所以我们在Controllers的里面进行业务层的装配并使用。我们以GuestbookInfo的列表显示为例,说一下。相应的Action叫说GuestbookList,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public ActionResult GuestbookList()
{
IList<GuestbookInfo> guestbookList = _biz.GetList();
return View(guestbookList);
}
private static IGuestbookBiz _biz = UnityHelper.UnityToT<IGuestbookBiz>("BizType");就是业务层装配了,我们通过业务接口IGuestbookBiz进行引用。通过_biz.GetList()就取得了相应的IList<GuestbookInfo>了。最后在相对应的View里面,指定Page Model ,像这样:Inherits="System.Web.Mvc.ViewPage<IList<Guestbook.Model.GuestbookInfo>>";然后在View中循环显示一下集合数据就可以了,大概像下面这样:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<table width="500" border="0" cellpadding="0" cellspacing="0">
<tr>
<th>姓名</th>
<th style="background-color:White"><%=item.GuestName %></th>
<th>性别</th>
<th style="background-color:White"><%=item.Sex==1?"男":"女" %></th>
</tr>
<tr>
<td colspan="4" height="50" valign="top">
<%=item.GuestMessage %>
</td>
</tr>
<tr>
<td colspan="4"><%=Html.ActionLink("删除","Delete",new{sysno=item.SysNo}) %></td>
</tr>
</table>
<%} %>
最后放一张运行的图片,其它的Save,Delete方式其本相同,这里就不多讲了。大家可以在这里下载源码(已上传)。