在企业级应用开发中,利用合适的框架针对不同项目构建相应的应用。Spring.Net作为Spring的.Net版本,具有很强大的应用。如:作为IoC容器、AOP处理框架、与NHibernate的集成、Spring.Net发布WebService\WCF服务等。而NHibernate作为一个优秀的ORM框架也广受开发者的青睐。本文将通过Spring.Net+NHibernate来构建WCF应用。
本文要点:
Spring.Net与NHibernate的集成应用
Spring.Net注入WCF
Entity与DTO对象之间的转化
先上一张结构图:
正式介绍之前介绍一下框架 版本:
Spring.Net :1.3.2
NHibernate :3.2(目前Spring支持的NHibernate最高版本)
1、 Spring.Net与NHibernate的集成应用
Spring.Net以及NHibernate的配置:
Spring.Net对NHibernate支持的配置(配置1):
<?
xml version="1.0" encoding="utf-8"
?>
<
objects
xmlns
="http://www.springframework.net"
xmlns:db
="http://www.springframework.net/database"
>
<!--配置数据库信息-->
<
db:provider
id
="dbProvider"
provider
="SqlServer-1.1"
connectionString
="Data Source=.\SqlExpress;Initial Catalog=SpringNHibernateCourseSystem;Integrated Security=True"
></
db:provider
>
<
object
id
="NHibernateSessionFactory"
type
="Spring.Data.NHibernate.LocalSessionFactoryObject,Spring.Data.NHibernate32"
>
<
property
name
="DbProvider"
ref
="dbProvider"
></
property
>
<!--配置NHibernate使用的实体程序集--
>
<
property
name
="MappingAssemblies"
>
<
list
>
<
value
> Domain
</
value
>
</
list
>
</
property
>
<!--配置NHibernate的配置信息--
>
<
property
name
="HibernateProperties"
>
<
dictionary
>
<
entry
key
="hibernate.connection.provider"
value
="NHibernate.Connection.DriverConnectionProvider"
></
entry
>
<
entry
key
="dialect"
value
="NHibernate.Dialect.MsSql2005Dialect"
></
entry
>
<
entry
key
="hibernate.connection.driver_class"
value
="NHibernate.Driver.SqlClientDriver"
></
entry
>
<
entry
key
="use_out_join"
value
="true"
></
entry
>
<
entry
key
="show_sql"
value
="true"
></
entry
>
<
entry
key
="hbm2ddl.auto"
value
="update"
></
entry
>
<
entry
key
="adonet.batch_size"
value
="10"
></
entry
>
<
entry
key
="command_timeout"
value
="10"
></
entry
>
<
entry
key
="cache.use_second_level_cache"
value
="true"
></
entry
>
<
entry
key
="cache.use_query_cache"
value
="true"
></
entry
>
<
entry
key
="query.substitutions"
value
="true 1, false 0, yes 'Y', no 'N"
></
entry
>
</
dictionary
>
</
property
>
<
property
name
="ExposeTransactionAwareSessionFactory"
value
="true"
></
property
>
</
object
>
<
object
id
="HibernateTemplate"
type
="Spring.Data.NHibernate.Generic.HibernateTemplate"
>
<
property
name
="SessionFactory"
ref
="NHibernateSessionFactory"
></
property
>
<
property
name
="TemplateFlushMode"
value
="Auto"
></
property
>
<
property
name
="CacheQueries"
value
="true"
></
property
>
</
object
>
</
objects
>
Spring.Net自身的配置(配置2):
View Code
1 <?xml version=
"
1.0
" encoding=
"
utf-8
" ?>
2 <objects xmlns=
"
http://www.springframework.net
" xmlns:db=
"
http://www.springframework.net/database
" >
3
4 <db:provider id=
"
dbProvider
" provider=
"
SqlServer-1.1
" connectionString=
"
Data Source=.\SqlExpress;Initial Catalog=SpringNHibernateCourseSystem;Integrated Security=True
" ></db:provider>
5 <
object id=
"
NHibernateSessionFactory
" type=
"
Spring.Data.NHibernate.LocalSessionFactoryObject,Spring.Data.NHibernate32
" >
6 <property name=
"
DbProvider
"
ref =
"
dbProvider
" ></property>
7 <property name=
"
MappingAssemblies
" >
8 <list>
9 <value>Domain</value>
10 </list>
11 </property>
12 <property name=
"
HibernateProperties
" >
13 <dictionary>
14 <entry key=
"
hibernate.connection.provider
" value=
"
NHibernate.Connection.DriverConnectionProvider
" ></entry>
15 <entry key=
"
dialect
" value=
"
NHibernate.Dialect.MsSql2005Dialect
" ></entry>
16 <entry key=
"
hibernate.connection.driver_class
" value=
"
NHibernate.Driver.SqlClientDriver
" ></entry>
17 <entry key=
"
use_out_join
" value=
"
true
" ></entry>
18 <entry key=
"
show_sql
" value=
"
true
" ></entry>
19 <entry key=
"
hbm2ddl.auto
" value=
"
update
" ></entry>
20 <entry key=
"
adonet.batch_size
" value=
"
10
" ></entry>
21 <entry key=
"
command_timeout
" value=
"
10
" ></entry>
22 <entry key=
"
cache.use_second_level_cache
" value=
"
true
" ></entry>
23 <entry key=
"
cache.use_query_cache
" value=
"
true
" ></entry>
24 <entry key=
"
query.substitutions
" value=
"
true 1, false 0, yes 'Y', no 'N
" ></entry>
25 </dictionary>
26 </property>
27 <property name=
"
ExposeTransactionAwareSessionFactory
" value=
"
true
" ></property>
28 </
object >
29
30 <
object id=
"
HibernateTemplate
" type=
"
Spring.Data.NHibernate.Generic.HibernateTemplate
" >
31 <property name=
"
SessionFactory
"
ref =
"
NHibernateSessionFactory
" ></property>
32 <property name=
"
TemplateFlushMode
" value=
"
Auto
" ></property>
33 <property name=
"
CacheQueries
" value=
"
true
" ></property>
34 </
object >
35
注意:在此配置中,<resource >节点引用其他程序集下的配置(同NHibernate的映射文件一样,被设置为"嵌入的资源"),请注意uri属性的配置方式。
NHibernate映射的配置(配置3):
<!--StudentEntity.hbm.xml 文件-->
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
<class name="StudentEntity" table="`Student`">
<id name="Id" type="Int32" >
<column name="ID" length="4" sql-type="int" not-null="true" unique="true" index="PK_Student"/>
<generator class="native" />
</id>
<property name="Name" type="String">
<column name="Name" length="50" sql-type="nvarchar" not-null="false"/>
</property>
<property name="Sex" type="String">
<column name="Sex" length="1" sql-type="char" not-null="false"/>
</property>
<many-to-one name="Class" class="ClassEntity " >
<column name="ClassId" length="4" sql-type="int" not-null="false"/>
</many-to-one>
</class>
</hibernate-mapping>
<!--ClassEntity.hbm.xml 文件-->
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain">
<class name="ClassEntity" table="`Class`">
<id name="Id" type="Int32">
<column name="ID" length="4" sql-type="int" not-null="true" unique="true" index="PK_Class"/>
<generator class="native" />
</id>
<property name="Name" type="String" length="50">
<column name="Name" length="50" sql-type="varchar" not-null="false"/>
</property>
<bag name="Students" inverse="true" lazy="true" cascade="all">
<key column="ClassID" foreign-key="FK_Student_Class"/>
<one-to-many class="StudentEntity"/>
</bag>
</class>
</hibernate-mapping>
Spring.Net与NHibernate整合后的持久化操作如下:
public class RepositoryBase
<
T
>
: HibernateDaoSupport, IRepository
<
T
>
where T : class
{ #region IRepository< T > 成员 public object Add(T entity) { return HibernateTemplate.Save(entity); } public void Delete(T entity) { HibernateTemplate.Delete(entity); } public void Update(T entity) { HibernateTemplate.Save(entity); } public T Get(int Id) { return HibernateTemplate.Get< T > (Id); } #endregion }
在Spring.Net中我们可以通过对RepositoryBase< T > 集成子Hibernate Dao Support进行注入。如上配置2中的
<
object
id
="classRepository"
type
="DAO.Implement.RepositoryBase<Domain.ClassEntity>,DAO"
>
<
property
name
="HibernateTemplate"
ref
="HibernateTemplate"
></
property
>
</
object
>
注意:在Spring.Net的配置中,对泛型类型中的"<"的配置是通过<配置的,因为Spring.Net会认为"<" 是小于号。而对">"则没有此类要求。
2、 Spring.Net注入WCF
上一篇 中已经介绍了Spring.Net对WCF支持以及客户端如何调用服务,本文不再做细致的介绍,只提几点需要注意的地方:
View Code
注意:service name="classService" 的配置中,name不再需要服务类的名称,而是用Spring.Net对服务配置的id即可。
服务寄宿:
static void Main(string[] args)
{ ContextRegistry.GetContext(); Console.WriteLine("Service Running..."); Console.ReadLine(); }
3、 Entity与DTO对象之间的转化
在NHibernate的应用中,一般是通过实体操作其映射的数据库表。因此此实体为不可或缺的。但通过应用层的逻辑处理后,展示给UI的可能是从不同的表中获取的信息,而UI层也不应用做一些业务处理;再者,如果将NHibernate使用的实体作为DTO也很可能面临传输了大量UI不需要的信息。所以想通过NHibernate的实体作为DTO(Data Transmit Object)肯定不是一种理想的方式。这样就面临一个问题:NHibernate的 Entity与DTO对象间如何转化? 通过 Client传输过来的DTO对象给Entity对象赋值肯定可行,但是这种类似"体力活"的方式显然不应该推崇。这里,我使用的是轻量、开源的AutoMapper。
Mapper.CreateMap< StudentDTO , StudentEntity > ();
Mapper.CreateMap
<
ClassDTO
, ClassEntity
> ()
.ForMember(item => item.Name, mapping => mapping.MapFrom(item => item.NO))
.ForMember(item=>item.Students,mapping=>mapping.MapFrom(item=>item.StudentDtos);
ClassEntity entity = Mapper.Map
<
ClassDTO
, ClassEntity
> (@class);
return Manager.Add(entity);
此外:通过还可以已通过Mapper.AssertConfigurationIsValid()来检验Entity与DTO之间映射是否正确。
服务操作:
public class ClassService : IContracts
{ public IClassManager Manager { get; set; } #region IContracts 成员 public object Add(ClassDTO @class) { Mapper.CreateMap< StudentDTO , StudentEntity > (); Mapper.CreateMap< ClassDTO , ClassEntity > () .ForMember(item => item.Name, mapping => mapping.MapFrom(item => item.NO)) .ForMember(item=>item.Students,mapping=>mapping.MapFrom(item=>item.StudentDtos); ClassEntity entity = Mapper.Map< ClassDTO , ClassEntity > (@class); return Manager.Add(entity); } #endregion }
同样,我也是通过 对服务类中的Manager属性进行注入,最终通过从IoC容器获取Manager对象来进行对象的持久化操作。注入如下:
<
object
id
="classService"
type
="Services.ClassService,Services"
>
<
property
name
="Manager"
ref
="classManager"
></
property
>
</
object
>
客户端调用:
IApplicationContext context = ContextRegistry.GetContext();
var proxy = context.GetObject(
"
classService
" )
as IContracts;
var dto = new ClassDTO { NO = " 035102A " }; List<StudentDTO> studentDtoList = new List<StudentDTO>() { new StudentDTO{Name=" StudentA " ,Sex=1 }, new StudentDTO{Name=" StudentB " ,Sex=0 }, }; dto.StudentDtos = studentDtoList; proxy.Add(dto); (proxy as ICommunicationObject).Abort():
结果:
题外:
不知道大家有没有碰到过:有时调试程序时,添加的断点有时会出现 "当前没有命中断点,还没有为该文档加载任何符号" 。下图:
在调试时可能出上述的调试过程中的问题:如下图:
我通过实践发现,这是由于引用的此程序集(程序集A)的目标程序集(程序集B)中,可能由于生成时过程中,新生成程序集A没有被拷贝到程序集B。导致这次问题的产生。若果程序集B所引用的为程序集A最新版本,则调试会是能正常进行的。如下图:
代码下载:http://download.csdn.net/detail/tyb1222/4425673