LayIM项目之基础数据获取代码优化,Dapper取代ADO.NET
前言
最近在开发LayIM融云版,也在进行项目重构,现在在看之前的代码,简直不敢直视。不过不知道以后看现在的代码是不是也是糟糕的一批。LayIM有个接口,一般接触过的开发人员都不会生疏,就是init接口。接口返回的数据大概就是酱紫的:
1 { 2 "code": 0, 3 "msg": "", 4 "data": { 5 "mine": { 6 "username": "纸飞机", 7 "id": "100000", 8 "status": "online", 9 "sign": "在深邃的编码世界,做一枚轻盈的纸飞机", 10 "avatar": "/images/default.jpg" 11 }, 12 "friend": [ 13 { 14 "groupname": "前端码屌", 15 "id": 1, 16 "online": 2, 17 "list": [ 18 { 19 "username": "贤心", 20 "id": "100001", 21 "avatar": "/images/default.jpg", 22 "sign": "这些都是测试数据,实际使用请严格按照该格式返回" 23 }, 24 { 25 "username": "Z_子晴", 26 "id": "108101", 27 "avatar": "/images/default.jpg", 28 "sign": "微电商达人" 29 } 30 ] 31 }, 32 { 33 "groupname": "网红", 34 "id": 2, 35 "online": 3, 36 "list": [ 37 { 38 "username": "罗玉凤", 39 "id": "121286", 40 "avatar": "/images/default.jpg", 41 "sign": "在自己实力不济的时候,不要去相信什么媒体和记者。他们不是善良的人,有时候候他们的采访对当事人而言就是陷阱" 42 }, 43 { 44 "username": "长泽梓Azusa", 45 "id": "100001222", 46 "sign": "我是日本女艺人长泽あずさ", 47 "avatar": "/images/default.jpg" 48 } 49 ] 50 } 51 ], 52 "group": [ 53 { 54 "groupname": "前端群", 55 "id": "101", 56 "avatar": "/images/default.jpg" 57 }, 58 { 59 "groupname": "Fly社区官方群", 60 "id": "102", 61 "avatar": "/images/default.jpg" 62 } 63 ] 64 } 65 }
总之,里面嵌套了很多关系,比如,我的好友分组和好友的关系,还有其他的一些数据,其实,仔细分析一下,也就会各个击破了。今天的重点不是这个数据,而是关于获取这段数据代码的重构。
之前的代码
之前用的ADO.NET直接读取的DataSet,然后进行数据处理,主要麻烦的是,还需要手动写DataTable转Model的过程,还要处理关系,比较繁琐,并且需要知道其中的字段。(当然,dapper也需要对应)。先看一下之前的代码,总之这段代码就是很古老的一种形式。
private BaseListResult ToBaseListResult(DataSet ds) { if (ds.Tables.Count > 0) { if (ds.Tables[0].Rows.Count ==0) { return new BaseListResult(); } //当前用户的信息 var rowMine = ds.Tables[0].Rows[0]; //用户组信息 var rowFriendDetails = ds.Tables[2].Rows.Cast<DataRow>().Select(x => new GroupUserEntity { id = x["uid"].ToInt(), avatar = x["avatar"].ToString(), groupid = x["gid"].ToInt(), remarkname = x["remarkname"].ToString(), username = x["nickname"].ToString(), sign = x["sign"].ToString(), //status之前的字段是为空的,现在我们把他的在线状态加上,IsOnline方法接收一个userid参数,从Redis缓存中读取该用户是否在线并返回 status = LayIMCache.Instance.IsOnline(x["uid"].ToInt()) ? "online" : "hide" }).OrderByDescending(x => x.status);//这里要根据用户是否在线这个字段排序,保证在线用户都在好友列表最上边 //用户组信息,执行分组 var friend = ds.Tables[1].Rows.Cast<DataRow>().Select(x => new FriendGroupEntity { id = x["id"].ToInt(), groupname = x["name"].ToString(), online = 0, list = rowFriendDetails.Where(f => f.groupid == x["id"].ToInt()) }); //群组信息 var group = ds.Tables[3].Rows.Cast<DataRow>().Select(x => new GroupEntity { id = x["id"].ToInt(), groupname = x["name"].ToString(), avatar = x["avatar"].ToString(), groupdesc = x["groupdesc"].ToString() }); //用户皮肤,第一个是默认正在使用的 List<string> skin = ds.Tables[4].Rows.Cast<DataRow>().Select(x => x[0].ToString()).ToList(); BaseListResult result = new BaseListResult { mine = new UserEntity { id = rowMine["id"].ToInt(), avatar = rowMine["avatar"].ToString(), sign = rowMine["sign"].ToString(), username = rowMine["nickname"].ToString(), status = "online" }, friend = friend, group = group, skin = skin }; return result; } return null; }
下面,我们改用Dapper试试。重构过程先不谈,看一下重构后的代码处理:
public BaseListResult Handle(SqlMapper.GridReader reader) { var result = new BaseListResult(); //用户本人数据 result.mine = reader.ReadFirst<UserEntity>(); //处理friend逻辑 start var friend = reader.Read<FriendGroupEntity>(); var groupUsers = reader.Read<GroupUserEntity>(); friend.ToList().ForEach(f => { //每一组的人分配给各个组 f.list = groupUsers?.Where(x => x.groupid == f.id); }); result.friend = friend; //处理friend逻辑 end //读取用户所在群 result.group = reader.Read<GroupEntity>(); return result; }
Dapper相较于ADO.NET比起来,就清爽多了,首先,处理GridReader,然后直接调用Read<T>方法,直接将表值转换为Model,不过Model的值需要对应。然后转换完之后,在进行一步逻辑处理,就是将相应的用户跟好友分组的id对应上。最后一个返回,这样看起来就清爽了许多,也不用处理的很麻烦。下文记录一下我的开发思路。
代码思路
首先要解决一个问题就是,解耦的问题,Dapper中有一个方法就是 QueryMultiple ,他是返回一个GridReader 对象,然后进行处理。那么这个GridReader对象又必须在连接开着的时候使用,所以,不能直接返回然后关闭对象,所以,可以采用接口的形式,将处理方法提出去,或者,我刚才恰好想到的就是用Func<SqlMapper.GridReader,TResult> 方式来处理结果。我的代码如下:
public static T QueryMultiple<T>(string sql, object param,CommandType commandType = CommandType.Text, IMultipleHandler<T> handler=null) { using (var connection = getConnection()) { using (var multi = connection.QueryMultiple(sql, param,commandType: commandType)) { if (handler == null) { return default(T); } return handler.Handle(multi); } } }
其中的IMultipleHandler 中就一个方法,Handle方法,参数为GridReader。然后返回 T类型的结果。所以上文中,我的调用方式很简单了,就是传入相应的处理类就可以了。
public class UserBaseListHandler : IMultipleHandler<BaseListResult>
其实我刚才突然想到,或许用Func更方便。不用再定义类继承接口了。此篇,Over,重构代码确实是有意思的事情,后续我会将各种体会做总结,如果能和读者产生共鸣那是最好不过的了。