使用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为例,代码如下:
CREATE TABLE `user`(
`Userid` INT NOT NULL AUTO_INCREMENT,
`Username` VARCHAR(100),
`Age` INT,
`City` VARCHAR(100), PRIMARY KEY (`Userid`)
); INSERT INTO `testex`.`user` (`Username`, `Age`, `City`) VALUES ('羊望', '', '厦门');
INSERT INTO `testex`.`user` (`Userid`, `Username`, `Age`, `City`) VALUES ('', '测试', '', '福州');
4.编写对应实体类
public class UserEntity
{
public int? Userid { get; set; } public string Username { get; set; } public int? Age { get; set; } public string City { get; set; }
}
5.写个简单的map文件:UserMap.xml
<?xml version="1.0" encoding="utf-8" ?>
<!--这里的namespace必须对应Dao接口的完整类名-->
<sqlMap namespace="IbatisExTest.Daos.IUserDao"
xmlns="http://ibatis.apache.org/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<alias>
<typeAlias alias="UserEntity" type="IbatisExTest.Entities.UserEntity,IbatisExTest" />
</alias> <statements> <select id="GetByUserid" parameterClass="String" resultClass="UserEntity">
SELECT *
FROM user
<dynamic prepend="WHERE">
Userid =#value#
</dynamic>
</select> <select id="GetByFilter" parameterClass="Hashtable" resultClass="UserEntity">
SELECT *
From user
<dynamic prepend="WHERE">
<isNotEmpty prepend="AND" property="Userid">
Userid =#Userid#
</isNotEmpty>
<isNotEmpty prepend="AND" property="Username">
Username =#Username#
</isNotEmpty>
</dynamic>
</select> <insert id="InsertUser" parameterClass="UserEntity">
INSERT INTO user
( Username
, Age
, City)
VALUES (
#Username#
, #Age#
, #City#);
</insert> </statements>
</sqlMap>
6.写一个接口,接口的全名(命名空间+接口名)必须与map文件的namespace相同,接口的方法与map文件中的sql片段id对应
public interface IUserDao
{ UserEntity GetByUserid(string userid); IList<UserEntity> GetByFilter(Hashtable ht); object InsertUser(UserEntity user);
}
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片段
/// <summary>
/// Dao接口的方法实现
/// </summary>
public class DaoInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
BaseDao baseDao = (BaseDao)invocation.Proxy;
//从基类BaseDao获取sqlMapper实例
ISqlMapper sqlMapper = baseDao.SqlMapper;
MethodInfo method = invocation.Method;
if (method.DeclaringType == null) return;
//获取接口的全名,即map文件的Namespace
string mapNamespace = method.DeclaringType.FullName;
//得到要执行的sql的完整id
string statementId = mapNamespace + "." + method.Name;
IMappedStatement ms = sqlMapper.GetMappedStatement(statementId);
if (ms is SelectMappedStatement)
{
ProcessSelectStatement(invocation, sqlMapper, statementId);
}
else if (ms is InsertMappedStatement)
{
ProcessInsertStatement(invocation, sqlMapper, statementId);
}
else if (ms is UpdateMappedStatement)
{
ProcessUpdateStatement(invocation, sqlMapper, statementId);
}
else if (ms is DeleteMappedStatement)
{
ProcessDeleteStatement(invocation, sqlMapper, statementId);
}
} private static void ProcessSelectStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
{
MethodInfo method = invocation.Method;
if (method.ReturnType.IsGenericType) //判断方法的返回值,如果是泛型,表示返回值是泛型集合
{
//通过反射调用sqlMapper.QueryForList方法
Type t = typeof(List<>).MakeGenericType(method.ReturnType.GetGenericArguments());
var list = Activator.CreateInstance(t);
MethodInfo miQueryForList = typeof(ISqlMapper).GetMethod("QueryForList",
new Type[] { typeof(string), typeof(object), typeof(List<>) });
miQueryForList.Invoke(sqlMapper, new object[] { statementId, invocation.Arguments[], list });
invocation.ReturnValue = list;
}
else //返回单个对象,或int等基本类型
{
//直接调用sqlMapper.QueryForObject方法
invocation.ReturnValue = sqlMapper.QueryForObject(statementId, invocation.Arguments[]);
}
} private static void ProcessInsertStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
{
invocation.ReturnValue = sqlMapper.Insert(statementId, invocation.Arguments[]);
} private static void ProcessUpdateStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
{
invocation.ReturnValue = sqlMapper.Update(statementId, invocation.Arguments[]);
} private static void ProcessDeleteStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
{
invocation.ReturnValue = sqlMapper.Delete(statementId, invocation.Arguments[]);
}
9.编写SqlMapper的扩展方法,动态实现Dao接口
public static class SqlMapExtensionMethods
{
/// <summary>
/// 获取Dao的实现
/// </summary>
/// <typeparam name="T">Dao接口</typeparam>
/// <param name="sqlMapper">sqlMapper</param>
/// <returns>返回Dao的实现</returns>
public static T GetDao<T>(this ISqlMapper sqlMapper)
{
ProxyGenerator generator = new ProxyGenerator();
DaoInterceptor daoInterceptor = new DaoInterceptor();
//创建一个BaseDao的代理类,并实现指定Dao接口
object proxy = generator.CreateClassProxy(typeof(BaseDao), new Type[] { typeof(T) }, ProxyGenerationOptions.Default, new object[] { sqlMapper }, daoInterceptor);
return (T)proxy;
}
}
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 = , 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,面向接口编程-更优雅的代码的更多相关文章
- Castle扩展Ibatis.Net
使用Castle扩展Ibatis.Net,面向接口编程-更优雅的代码 使用Ibatis.Net做项目半年了,甚是喜欢,感觉确实是个简单.轻巧的O/R Mapping框架,特别是将Sql配置在Xml文件 ...
- 9条消除if...else的锦囊妙计,助你写出更优雅的代码
前言 最近在做代码重构,发现了很多代码的烂味道.其他的不多说,今天主要说说那些又臭又长的if...else要如何重构. 在介绍更更优雅的编程之前,让我们一起回顾一下,不好的if...else代码 一. ...
- 更优雅的清除浮动float方法
上篇文章是利用 :after 方法清除浮动float(作用于浮动元素的父元素上). ; } //为了兼容性,因为ie6/7不能使用伪类,所以加上此行代码. .outer:after {;;visibi ...
- 设计与实现分离——面向接口编程(OO博客第三弹)
如果说继承是面向对象程序设计中承前启后的特质,那么接口就是海纳百川的体现了.它们都是对数据和行为的抽象,都是对性质和关系的概括.只不过前者是纵向角度,而后者是横向角度罢了.今天呢,我想从设计+语法角度 ...
- C#扫盲篇(二)依赖倒置•控制反转•依赖注入•面向接口编程--满腹经纶的说
扫盲系列的文章收到了广大粉丝朋友的支持,十分感谢,你们的支持就是我最大动力. 我的扫盲系列还会继续输出,本人也是一线码农,有什么问题大家可以一起讨论.也可以私信或者留言您想要了解的知识点,我们一起进步 ...
- C#进阶系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程)
序:忙碌多事的八月带着些许的倦意早已步入尾声,金秋九月承载着抗战胜利70周年的喜庆扑面而来.没来得及任何准备,似乎也不需要任何准备,因为生活不需要太多将来时.每天忙着上班.加班.白加班,忘了去愤,忘了 ...
- C#面向接口编程详解(1)——思想基础
我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有没有这样的疑惑:接口有什么用途?它和抽象类有什么区别?能不能用抽象类代替接口呢?而且,作为程序员,一定经常听到 ...
- 面向接口编程详解-Java篇
相信看到这篇文字的人已经不需要了解什么是接口了,我就不再过多的做介绍了,直接步入正题,接口测试如何编写.那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象.为充分考虑到初学者,所 ...
- IOC解耦-面向接口编程的优点
原文:https://blog.csdn.net/jj_nan/article/details/70161086 参考:https://www.cnblogs.com/landeanfen/p/477 ...
随机推荐
- S关于使用QL声明 找出同时满足多个tag拍摄条件设置算法
表结构 Tag Table:{tag_id, tag_name} #标签表 News Table:{news_id, title,......} #新闻列表 NewsTags Table:{tag ...
- opencv在arm和x86在移植
一个.开发环境 操作系统:fedora14 Opencv版本号:2.0 Qt版本号:4.7 arm:mini6410 交叉编译工具:arm-linux-gcc-4.5.1 二.安装与配置 Linux系 ...
- 一个好用的web甘特图
前些天一直在弄web甘特图,发现网上很多web甘特图框架,但大部分是收费的.偶尔发现了向日葵甘特图 感觉不错,特此写下来一方面当做记录,另一方面也为寻找web甘特图的同学们少走一些弯路,双赢嘛~ ...
- JavaEE(16) - JPA生命周期及监听器
1. 理解实体的生命周期 2. 为实体生命周期事件定义监听器 3. 通过监听实现回调 4. 排除默认监听器和父类上定义的监听器 1. 理解实体的生命周期(Net Beans创建Java Project ...
- unity 编辑器和插件生产(四.2)
上次 我们告诉编辑器制作,如何将图像加载到现场,如今 我们要告诉下.怎么样 制造UIButton以及UIimage交换. 阿土. 进入专题. 首先,我们要明白 unity机制.button属性等. 首 ...
- AT&T汇编——在你开始写
不知不觉,少年将超过,计算机相关知识,学到基本上可以说是教过.毕业.所以,我们打算更深入了解自己的兴趣背着背笼.也因为它是检讨大学. 计划写的内容在: 1.汇编语言 2.C/C++语言 3.Linux ...
- 至linuxNIC添加多个IP
由于工作的需要,只是有一个2以太网端口server构造3个月IP.制linux. 整理如以下的现在的过程,有离开后,,学生们也将不能够引用. IP1:10.110.97.68 IP2:10.115.2 ...
- 浅谈JavaScript中继承的实现
谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我 ...
- Java经典23创意模式设计模式(两)
本文介绍5其余两种创意模式:模型构建器(Builder).原型模型(PROTOTYPE). 一.建造者模式(别名:生成者模式) 将复杂对象的构建和它的表示分离,使得相同的构建过程能够创建不同的表示. ...
- BZOJ 1355 Baltic2009 Radio Transmission KMP算法
标题效果:给定一个字符串,求最小周期节(不能整除) 示例Hint这是错误的忽略了就好了 环路部分应该是cab 这个称号充分利用KMP在next自然阵列,那是,n-next[n]它表示一个循环节 POJ ...