一步步实现自己的ORM(三)
章节列表:
通过前面两篇文章,我们大致了解了ORM的基本原理,是通过Attribute+反射获取表的基本信息,再用表名和字段名拼接成SQL语句。而前面我们只是完成了CRUD部分的CUD,还没完成读取(Retrieve)操作,今天就来完成这个R操作。我们先看下平时写实体类转换代码是怎么写的:
static List<User> ToObject1() { SqlDataReader reader = null; List<User> list = new List<User>(); while (reader.Read()) { User user = new User(); if (reader["UserId"] != null) user.UserId = (int)reader["UserId"]; if (reader["Email"] != null) user.Email = (string)reader["Email"]; if (reader["CreatedTime"] != null) user.CreatedTime = (DateTime)reader["CreatedTime"]; list.Add(user); } return list; }
经常写DataReader对象转换成实体类的童鞋们,WHILE循环里的内容是根据实体类的不同代码而有所不同,循环外的代码则是固定不变的,现在我们要把变化的代码抽离出来,我们还是请出明星--反射。
在前面章节我们都是用反射读取实体类里的,比如typeof(User).GetProperty("UserId").GetValue(obj,null),有GetValue肯定也有SetValue,我们看下SetValue的定义:
public virtual void SetValue( Object obj, Object value, Object[] index )
obj:将设置其属性值的对象。
value:新的属性值。
index : 索引化属性的可选索引值。 对于非索引化属性,该值应为 null。
示例代码:
User user = new User(); typeof(User).GetProperty("UserId").SetValue(user, 10, null); Console.WriteLine(user.UserId);
输出结果我就不贴图了,是“10”。
HOHO,我们开始构建代码,步骤如下:
1. new 实体类对象
2. 反射类的属性,然后用SetValue给实体类属性赋值
具体代码如下:
string sql = "select * from tb_users"; SqlConnection conn = new SqlConnection(EntityHelper.connectionString); conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = sql; SqlDataReader reader = cmd.ExecuteReader(); List<User> list = new List<User>(); var type = typeof(User); var properties = type.GetProperties(); while (reader.Read()) { User user = new User(); for (int i = 0; i < properties.Length; i++) { var pi = properties[i]; if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句 pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"]; } list.Add(user); } foreach (var item in list) { Console.WriteLine("UserId:{0},Email:{1},CreatedTime:{2}", item.UserId, item.Email, item.CreatedTime); }
运行结果:
下面一个问题来了,我们看这一行代码
User user = new User();
难道我们每次都要这么写这样的代码吗,能不能做一个通用的吗?这需要我们用反射实例化一个类,我们请Activator.CreateInstance帮忙创建实例,代码如下:
var type = typeof(User); var user = Activator.CreateInstance(type);
创建实例和给属性赋值都动态完成,我稍稍修改下代码,只要传递SQL语句就可以返回集合。
static List<T> GetEntityList<T>(string sql) { SqlConnection conn = new SqlConnection(EntityHelper.connectionString); conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = sql; SqlDataReader reader = cmd.ExecuteReader(); List<T> list = new List<T>(); var type = typeof(T); var properties = type.GetProperties(); while (reader.Read()) { var user = Activator.CreateInstance(type); for (int i = 0; i < properties.Length; i++) { var pi = properties[i]; if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句 pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"]; } list.Add((T)user); } return list; }
最后我们在增加如下方法:
Get(object[] values) 根据主键获取实体类
GetList<T>(string where,string orderBy) 按条件查询实体类
public static T Get<T>(object[] values) { var type = typeof(T); Dictionary<string, object> parameters = new Dictionary<string, object>(); var properties = type.GetProperties(); string tableName = string.Empty; TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true); if (tableAttrs.Length > 0) { tableName = tableAttrs[0].Name; } else { tableName = type.Name; } /*将所有的列放到集合里*/ List<IdAttribute> columns = new List<IdAttribute>(); for (int i = 0; i < properties.Length; i++) { var pi = properties[i]; var attrs = (IdAttribute[])pi.GetCustomAttributes(typeof(IdAttribute), true); if (attrs.Length > 0) { columns.Add(attrs[0]); } } if (columns.Count != values.Length) throw new ArgumentException("参数个数和主键数不一致"); StringBuilder sql = new StringBuilder(); sql.Append("SELECT * FROM [").Append(tableName).Append("] ").Append(" WHERE "); for (int i = 0; i < columns.Count; i++) { if (i > 0) //考虑到有多个主键 sql.Append(" AND "); sql.Append(columns[i].Name).Append("=").Append("@p").Append(i); /*参数*/ parameters.Add("@p" + i, values[i]); } Console.WriteLine(sql); return GetEntityList<T>(sql.ToString(), parameters).FirstOrDefault(); } public static List<T> GetList<T>(string where,string orderBy) { var type = typeof(T); Dictionary<string, object> parameters = new Dictionary<string, object>(); var properties = type.GetProperties(); string tableName = string.Empty; TableAttribute[] tableAttrs = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), true); if (tableAttrs.Length > 0) { tableName = tableAttrs[0].Name; } else { tableName = type.Name; } StringBuilder sql = new StringBuilder(); sql.Append("SELECT * FROM [").Append(tableName).Append("] "); if (string.IsNullOrEmpty(where)) sql.Append(" WHERE ").Append(where); if (string.IsNullOrEmpty(orderBy)) sql.Append(" Order By ").Append(orderBy); Console.WriteLine(sql); return GetEntityList<T>(sql.ToString(), parameters); } public static List<T> GetEntityList<T>(string sql, Dictionary<string, object> parameters) { SqlConnection conn = new SqlConnection(EntityHelper.connectionString); conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = sql; if (parameters != null && parameters.Count > 0) { foreach (var item in parameters) { var pa = cmd.CreateParameter(); pa.ParameterName = item.Key; pa.Value = item.Value ?? DBNull.Value; cmd.Parameters.Add(pa); } } SqlDataReader reader = cmd.ExecuteReader(); List<T> list = new List<T>(); var type = typeof(T); var properties = type.GetProperties(); while (reader.Read()) { var user = Activator.CreateInstance(type); for (int i = 0; i < properties.Length; i++) { var pi = properties[i]; if (reader[pi.Name] != null) //等同于 if (reader["UserId"] != null)这样的语句 pi.SetValue(user, reader[pi.Name], null); //等同于 user.UserId = (int)reader["UserId"]; } list.Add((T)user); } return list; }
下载代码:https://files.cnblogs.com/files/sobaby/ORM03.zip
EntityHelper里重复代码很多,而且采用反射效率很低,下一章就开始优化代码。