使用Castle扩展Ibatis.Net,面向接口编程-更优雅的代码

使用Ibatis.Net做项目半年了,甚是喜欢,感觉确实是个简单、轻巧的O/R Mapping框架,特别是将Sql配置在Xml文件中,相当于直接将Dao层抽离了出来。

本文假定读者对Ibatis.Net有一定的了解。

最近试用了一下Ibatis.Net的亲兄弟--Java的Mybatis,一对比发现:

执行一个查询,Ibatis.Net是这么写的:IList<UserEntity> list = SqlMapper.QueryForList<UserEntity>(prefix+ ".GetByFilter", parameters);

而Java的Mybatis是这么写的:List<UserEntity> list = dao.GetByFilter(parameters);

发现了没,后者的显然更优雅。

Mybatis之所以能这么调用,是因为Mybatis提供了一种面向接口编程的方法,只要写好接口,接口的方法名与map文件中sql片段的id相同,我们就能够直接通过接口调用。

我想了又想...C#也能够实现这样优雅的调用啊,可是为啥Ibatis.Net不提供呢,想到这,我开始明白Ibatis.Net是后妈生的。。。


说到这,进入主题吧,既然Ibatis.Net先天不够强大,那我们后天弥补吧,这里主要使用Castle这个组件来动态实现接口。

接下来我们做个Demo

1.搭建Ibatis.Net环境,这里就不说啦(最新版Ibatis.Net下载地址:http://download.csdn.net/detail/tzjzcy/7829759 )

2.引用Castle.Core.dll,这个dll实际上最新版的Ibatis.Net本身就有用到

3.创建一个测试表,录入数据,本文以mysql为例,代码如下:

1 CREATE TABLE `user`(
2 `Userid` INT NOT NULL AUTO_INCREMENT,
3 `Username` VARCHAR(100),
4 `Age` INT,
5 `City` VARCHAR(100), PRIMARY KEY (`Userid`)
6 );
7
8 INSERT INTO `testex`.`user` (`Username`, `Age`, `City`) VALUES ('羊望', '26', '厦门');
9 INSERT INTO `testex`.`user` (`Userid`, `Username`, `Age`, `City`) VALUES ('2', '测试', '18', '福州');

4.编写对应实体类

 1     public class UserEntity
2 {
3 public int? Userid { get; set; }
4
5 public string Username { get; set; }
6
7 public int? Age { get; set; }
8
9 public string City { get; set; }
10 }

5.写个简单的map文件:UserMap.xml

 1 <?xml version="1.0" encoding="utf-8" ?>
2 <!--这里的namespace必须对应Dao接口的完整类名-->
3 <sqlMap namespace="IbatisExTest.Daos.IUserDao"
4 xmlns="http://ibatis.apache.org/mapping"
5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
6 <alias>
7 <typeAlias alias="UserEntity" type="IbatisExTest.Entities.UserEntity,IbatisExTest" />
8 </alias>
9
10 <statements>
11
12 <select id="GetByUserid" parameterClass="String" resultClass="UserEntity">
13 SELECT *
14 FROM user
15 <dynamic prepend="WHERE">
16 Userid =#value#
17 </dynamic>
18 </select>
19
20 <select id="GetByFilter" parameterClass="Hashtable" resultClass="UserEntity">
21 SELECT *
22 From user
23 <dynamic prepend="WHERE">
24 <isNotEmpty prepend="AND" property="Userid">
25 Userid =#Userid#
26 </isNotEmpty>
27 <isNotEmpty prepend="AND" property="Username">
28 Username =#Username#
29 </isNotEmpty>
30 </dynamic>
31 </select>
32
33 <insert id="InsertUser" parameterClass="UserEntity">
34 INSERT INTO user
35 ( Username
36 , Age
37 , City)
38 VALUES (
39 #Username#
40 , #Age#
41 , #City#);
42 </insert>
43
44 </statements>
45 </sqlMap>

6.写一个接口,接口的全名(命名空间+接口名)必须与map文件的namespace相同,接口的方法与map文件中的sql片段id对应

1     public interface IUserDao
2 {
3
4 UserEntity GetByUserid(string userid);
5
6 IList<UserEntity> GetByFilter(Hashtable ht);
7
8 object InsertUser(UserEntity user);
9 }

7.写一个BaseDao,作为动态创建的Dao实现类的基类,定义一个属性,传入SqlMapper用

    public class BaseDao
{
public BaseDao(ISqlMapper sqlMapper)
{
this.SqlMapper = sqlMapper;
}
public ISqlMapper SqlMapper { get; private set; }
}

8.重点来了,编写Dao实现类的具体方法实现,通过Castle组件实现的,作用是:在调用接口的方法时,执行map中对应的sql片段

 1     /// <summary>
2 /// Dao接口的方法实现
3 /// </summary>
4 public class DaoInterceptor : IInterceptor
5 {
6 public void Intercept(IInvocation invocation)
7 {
8 BaseDao baseDao = (BaseDao)invocation.Proxy;
9 //从基类BaseDao获取sqlMapper实例
10 ISqlMapper sqlMapper = baseDao.SqlMapper;
11 MethodInfo method = invocation.Method;
12 if (method.DeclaringType == null) return;
13 //获取接口的全名,即map文件的Namespace
14 string mapNamespace = method.DeclaringType.FullName;
15 //得到要执行的sql的完整id
16 string statementId = mapNamespace + "." + method.Name;
17 IMappedStatement ms = sqlMapper.GetMappedStatement(statementId);
18 if (ms is SelectMappedStatement)
19 {
20 ProcessSelectStatement(invocation, sqlMapper, statementId);
21 }
22 else if (ms is InsertMappedStatement)
23 {
24 ProcessInsertStatement(invocation, sqlMapper, statementId);
25 }
26 else if (ms is UpdateMappedStatement)
27 {
28 ProcessUpdateStatement(invocation, sqlMapper, statementId);
29 }
30 else if (ms is DeleteMappedStatement)
31 {
32 ProcessDeleteStatement(invocation, sqlMapper, statementId);
33 }
34 }
35
36 private static void ProcessSelectStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
37 {
38 MethodInfo method = invocation.Method;
39 if (method.ReturnType.IsGenericType) //判断方法的返回值,如果是泛型,表示返回值是泛型集合
40 {
41 //通过反射调用sqlMapper.QueryForList方法
42 Type t = typeof(List<>).MakeGenericType(method.ReturnType.GetGenericArguments());
43 var list = Activator.CreateInstance(t);
44 MethodInfo miQueryForList = typeof(ISqlMapper).GetMethod("QueryForList",
45 new Type[] { typeof(string), typeof(object), typeof(List<>) });
46 miQueryForList.Invoke(sqlMapper, new object[] { statementId, invocation.Arguments[0], list });
47 invocation.ReturnValue = list;
48 }
49 else //返回单个对象,或int等基本类型
50 {
51 //直接调用sqlMapper.QueryForObject方法
52 invocation.ReturnValue = sqlMapper.QueryForObject(statementId, invocation.Arguments[0]);
53 }
54 }
55
56 private static void ProcessInsertStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
57 {
58 invocation.ReturnValue = sqlMapper.Insert(statementId, invocation.Arguments[0]);
59 }
60
61 private static void ProcessUpdateStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
62 {
63 invocation.ReturnValue = sqlMapper.Update(statementId, invocation.Arguments[0]);
64 }
65
66 private static void ProcessDeleteStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
67 {
68 invocation.ReturnValue = sqlMapper.Delete(statementId, invocation.Arguments[0]);
69 }

9.编写SqlMapper的扩展方法,动态实现Dao接口

 1     public static class SqlMapExtensionMethods
2 {
3 /// <summary>
4 /// 获取Dao的实现
5 /// </summary>
6 /// <typeparam name="T">Dao接口</typeparam>
7 /// <param name="sqlMapper">sqlMapper</param>
8 /// <returns>返回Dao的实现</returns>
9 public static T GetDao<T>(this ISqlMapper sqlMapper)
10 {
11 ProxyGenerator generator = new ProxyGenerator();
12 DaoInterceptor daoInterceptor = new DaoInterceptor();
13 //创建一个BaseDao的代理类,并实现指定Dao接口
14 object proxy = generator.CreateClassProxy(typeof(BaseDao), new Type[] { typeof(T) }, ProxyGenerationOptions.Default, new object[] { sqlMapper }, daoInterceptor);
15 return (T)proxy;
16 }
17 }

10.这样就完成了扩展,让我们看看调用实例吧

    class Program
{
private const string mapperNamespace = "IbatisExTest.Daos.IUserDao"; private static ISqlMapper SqlMapper
{
get { return Mapper.Get(); }
} private static IUserDao UserDao
{
get { return SqlMapper.GetDao<IUserDao>(); }
} static void Main()
{
Hashtable ht = new Hashtable();
ht["Username"] = "羊望";
//传统用法
IList<UserEntity> list1 = SqlMapper.QueryForList<UserEntity>(mapperNamespace + ".GetByFilter", ht); //新用法(代码更优雅了吧)
IList<UserEntity> list2 = UserDao.GetByFilter(ht); //测试新增
//UserEntity user = new UserEntity { Username = "新用户", Age = 11, City = "新城市" };
//UserDao.InsertUser(user);
}
}

最后,我们看到,扩展后我们只需要调用Dao接口的方法,代码更简洁了。

至于要比传统用法多写个Dao接口,这个工作或许我们可以通过代码生成工具来做吧。

源代码下载:http://files.cnblogs.com/lookup/Castle%E6%89%A9%E5%B1%95IbatisNet%E4%BE%8B%E5%AD%90.zip

欢迎拍砖:)

 

Castle扩展Ibatis.Net的更多相关文章

  1. 使用Castle扩展Ibatis.Net,面向接口编程-更优雅的代码

    使用Ibatis.Net做项目半年了,甚是喜欢,感觉确实是个简单.轻巧的O/R Mapping框架,特别是将Sql配置在Xml文件中,相当于直接将Dao层抽离了出来. 本文假定读者对Ibatis.Ne ...

  2. 关于ibatis进行物理游标分页

    http://www.iteye.com/topic/136712 详细demo:参照http://www.kusoft.net 我的数据库是采用mssql2000 采用分页必定数据量比较大: 按照i ...

  3. 读书笔记--iBATIS in Action 目录

    1.iBATIS的理念 2.iBATIS是什么 3.安装和配置iBATIS 4.使用以映射语句 5.执行非查询语句 6.使用高级查询技术 7.事务 8.使用动态SQL 9.使用高速缓存提高性能 10. ...

  4. ORM 框架简介

    对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应 ...

  5. ORM(Object/Relation Mapping)框架简介

    ORM 框架简介 对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关 ...

  6. Ibatis学习总结4--SQL Map XML 映射文件扩展

    SQL Map XML 映射文件除了上文提到的属性还有一些其他重要的属性,下文将详细介绍这些属性. 缓存 Mapped Statement 结果集 通过在查询 statement 中指定 cacheM ...

  7. Castle Core 4.0.0 alpha001发布

    时隔一年多以后Castle 项目又开始活跃,最近刚发布了Castle Core 4.0.0 的alpha版本, https://github.com/castleproject/Core/releas ...

  8. ASP.NET Core 整合Autofac和Castle实现自动AOP拦截

    前言: 除了ASP.NETCore自带的IOC容器外,我们还可以使用其他成熟的DI框架,如Autofac,StructureMap等(笔者只用过Unity,Ninject和Castle). 1.ASP ...

  9. Hibernate和IBatis对比

    [转自]http://blog.csdn.net/ya2dan/article/details/7396598 项目也做过几个, 使用IBatis就做一个项目, 基本上都是使用Hibernate, 也 ...

随机推荐

  1. NET MVC

    NET MVC 1.为 Action 标注 Attribute 限制访问 public class HomeController : Controller { [HttpPost] public Ac ...

  2. hdu2844 &amp; poj1742 Coin ---多重背包--两种方法

    意甲冠军:你有N种硬币,每个价格值A[i],每个号码C[i],要求. 在不超过M如果是,我们用这些硬币,有多少种付款的情况下,.那是,:1,2,3,4,5,....,M这么多的情况下,,你可以用你的硬 ...

  3. Python 基于学习 网络小爬虫

    <span style="font-size:18px;"># # 百度贴吧图片网络小爬虫 # import re import urllib def getHtml( ...

  4. SQL Server 2005 MD5函数

    原文:SQL Server 2005 MD5函数 在SQL Server 2005下自带的函数HashBytes() ,此函数是微软在SQL Server 2005中提供的,可以用来计算一个字符串的M ...

  5. ODP.NET 之访问 Oracle 数据库

    ODP.NET 之访问 Oracle 数据库 要相使用 Oracle Data Provider For .NET(ODP.NET), 必须先安装 ODP.NET 或者是 ODAC(Oracle Da ...

  6. 假设动态运行java文字,当在脚本式配置,这是非常方便的

    package com.bfrj.core.groovy; import java.util.HashMap; import java.util.Map; import org.jeecgframew ...

  7. Repository模式

    Repository模式的两种写法与疑惑 现如今DDD越来越流行,园子里漫天都是介绍关于它的文章.说到DDD就不能不提Repository模式了,有的地方也叫它仓储模式. 很多时候我们对Reposit ...

  8. NPOI实现Excel导入导出

    NPOI实现Excel的导入导出,踩坑若干. Cyan是博主[Soar360]自2014年以来开始编写整理的工具组件,用于解决现实工作中常用且与业务逻辑无关的问题. 什么是NPOI? NPOI 是 P ...

  9. crawler_google工作原理

  10. 【百度地图API】如何制作公交线路的搜索?如331路

    原文:[百度地图API]如何制作公交线路的搜索?如331路 摘要: 从A点到B点的公交导航大家都知道怎么做了,那么单独查询331路公交车的公交路线,如何制作呢?我们一起来学习一下~ --------- ...