Castle扩展Ibatis.Net

使用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

欢迎拍砖:)

 

 

 

 
标签:  CastleIbatis.Net面向接口

你可能感兴趣的:(面向接口,Castle,Ibatis.Net)