2016新年伊始,望眼过去,不知不觉在博客园已经注册8个月啦,由于最近忙于工作,博客迟迟没有更新。直到最近一直研究.Net设计模式,对一些模式有所感悟,故拿出自己的心得与大家分享,在接下来的所有博客中我都会以【理论介绍】和【具体实践】两个方面来叙述对模式的应用。
仓储(Respository)是存在于工作单元和数据库之间单独分离出来的一层,是对数据访问的封装。其优点:
1)业务层不需要知道它的具体实现,达到了分离关注点。
2)提高了对数据库访问的维护,对于仓储的改变并不会改变业务的逻辑,数据库可以用Sql Server(该系列博客使用)、MySql等。
首先,创建IRepository.cs接口定义对数据操作的契约(命令执行、重载不同查询结果集合、查询实体、查询返回结果值)。
1 /// <summary> 2 /// Ado.Net实现的仓储 3 /// </summary> 4 public interface IRepository 5 { 6 /// <summary> 7 /// 增、删、改对象 8 /// </summary> 9 /// <param name="commandText">Sql语句</param> 10 /// <param name="parameters">参数</param> 11 /// <param name="isCommit">是否提交</param> 12 /// <returns></returns> 13 void Command(string commandText, IDictionary<string, object> parameters = null); 14 15 /// <summary> 16 /// 查询对象集合 17 /// </summary> 18 /// <typeparam name="T">返回值的实体类型</typeparam> 19 /// <param name="commandText">Sql语句</param> 20 /// <param name="parameters">参数</param> 21 /// <param name="isCommit">是否提交</param> 22 /// <returns>泛型实体集合</returns> 23 List<T> QueryAll<T>(string commandText, IDictionary<string, object> parameters = null) where T : class, new(); 24 25 /// <summary> 26 /// 查询对象集合 27 /// </summary> 28 /// <param name="commandText"></param> 29 /// <param name="type"></param> 30 /// <param name="parameters"></param> 31 /// <param name="isCommit"></param> 32 /// <returns></returns> 33 List<object> QueryAll(string commandText, Type type, IDictionary<string, object> parameters = null); 34 35 /// <summary> 36 /// 查询对象 37 /// </summary> 38 /// <typeparam name="TEntity"></typeparam> 39 /// <param name="commandText"></param> 40 /// <param name="type"></param> 41 /// <param name="parameters"></param> 42 /// <returns></returns> 43 T Query<T>(string commandText, IDictionary<string, object> parameters = null) where T : class,new(); 44 45 /// <summary> 46 /// 查询数量 47 /// </summary> 48 /// <param name="commandText"></param> 49 /// <param name="parameters"></param> 50 /// <returns></returns> 51 object QueryCount(string commandText, IDictionary<string, object> parameters = null); 52 }
其次,定义抽象类BaseRepository.cs实现仓储。
1 /// <summary> 2 /// 基础库实现 3 /// </summary> 4 public abstract class BaseRepository : IRepository 5 { 6 /// <summary> 7 /// 数据库连接字符串标识 8 /// </summary> 9 public abstract string Key { get; } 10 11 private SqlConnection connection; 12 13 private SqlConnection Connection 14 { 15 get 16 { 17 if (connection == null) 18 { 19 ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[Key]; 20 connection = new SqlConnection(settings.ConnectionString); 21 } 22 return connection; 23 } 24 } 25 26 /// <summary> 27 /// 增、删、改命令 28 /// </summary> 29 /// <param name="commandText"></param> 30 /// <param name="parameters"></param> 31 public virtual void Command(string commandText, IDictionary<string, object> parameters = null) 32 { 33 Func<SqlCommand, int> excute = (commend) => 34 { 35 return commend.ExecuteNonQuery(); 36 }; 37 CreateDbCommondAndExcute<int>(commandText, parameters, excute); 38 } 39 40 /// <summary> 41 /// 查询实体(强类型) 42 /// </summary> 43 /// <typeparam name="T"></typeparam> 44 /// <param name="commandText"></param> 45 /// <param name="parameters"></param> 46 /// <returns></returns> 47 public virtual T Query<T>(string commandText, IDictionary<string, object> parameters = null,Func<IDataReader, T> load = null) where T : class, new() 48 { 49 Func<SqlCommand, T> excute = (dbCommand) => 50 { 51 52 var result = default(T); 53 using (IDataReader reader = dbCommand.ExecuteReader()) 54 { 55 while (reader.Read()) 56 { 57 if (load == null) 58 { 59 load = (s) => { return s.GetReaderData<T>(); }; 60 } 61 result = load(reader); 62 } 63 return result; 64 } 65 }; 66 return CreateDbCommondAndExcute<T>(commandText, parameters, excute); 67 } 68 69 /// <summary> 70 /// 查询匿名对象集合 71 /// </summary> 72 /// <param name="commandText"></param> 73 /// <param name="type"></param> 74 /// <param name="parameters"></param> 75 /// <returns></returns> 76 public virtual List<object> QueryAll(string commandText, Type type, IDictionary<string, object> parameters = null,Action<dynamic> setItem = null) 77 { 78 Func<SqlCommand, List<object>> excute = (dbCommand) => 79 { 80 var result = new List<object>(); 81 82 using (IDataReader dataReader = dbCommand.ExecuteReader()) 83 { 84 while (dataReader.Read()) 85 { 86 var item = dataReader.GetReaderData(type); 87 if (setItem != null) 88 { 89 setItem(item); 90 } 91 result.Add(item); 92 } 93 } 94 return result; 95 }; 96 return CreateDbCommondAndExcute<List<object>>(commandText, parameters, 97 excute); 98 } 99 100 /// <summary> 101 /// 查询强类型对象集合 102 /// </summary> 103 /// <typeparam name="T"></typeparam> 104 /// <param name="commandText"></param> 105 /// <param name="parameters"></param> 106 /// <returns></returns> 107 public virtual List<T> QueryAll<T>(string commandText, IDictionary<string, object> parameters = null,Func<IDataReader, T> load = null) where T : class, new() 108 { 109 Func<SqlCommand, List<T>> excute = (dbCommand) => 110 { 111 List<T> result = new List<T>(); 112 using (IDataReader reader = dbCommand.ExecuteReader()) 113 { 114 while (reader.Read()) 115 { 116 if (load == null) 117 { 118 load = (s) => { return s.GetReaderData<T>(); }; 119 } 120 var item = load(reader); 121 result.Add(item); 122 } 123 return result; 124 } 125 }; 126 return CreateDbCommondAndExcute(commandText, parameters, excute); 127 } 128 129 /// <summary> 130 /// 查询结果数量 131 /// </summary> 132 /// <param name="commandText"></param> 133 /// <param name="parameters"></param> 134 /// <returns></returns> 135 public virtual object QueryCount(string commandText, IDictionary<string, object> parameters = null) 136 { 137 Func<SqlCommand, object> excute = (dbCommand) => 138 { 139 return dbCommand.ExecuteScalar(); 140 }; 141 return CreateDbCommondAndExcute(commandText, parameters, excute); 142 } 143 144 /// <summary> 145 /// 创建命令并执行 146 /// </summary> 147 /// <typeparam name="TValue"></typeparam> 148 /// <param name="commandText"></param> 149 /// <param name="parameters"></param> 150 /// <param name="excute"></param> 151 /// <returns></returns> 152 private TValue CreateDbCommondAndExcute<TValue>(string commandText, 153 IDictionary<string, object> parameters, Func<SqlCommand, TValue> excute) 154 { 155 if (Connection.State == ConnectionState.Closed) { Connection.Open(); }; 156 using (SqlCommand command = new SqlCommand()) 157 { 158 command.CommandType = CommandType.Text; 159 command.CommandText = commandText;; 160 command.Connection = Connection; 161 command.SetParameters(parameters); 162 return excute(command); 163 } 164 } 165 166 /// <summary> 167 /// 关闭连接 168 /// </summary> 169 public void Dispose() 170 { 171 if (connection != null) 172 { 173 Connection.Dispose();//非托管资源 174 } 175 } 176 }
最后,实现方法中的扩展方法。
1 /// <summary> 2 /// Sql Server 扩展类 3 /// </summary> 4 public static class SqlServerExtension 5 { 6 /// <summary> 7 /// 设置参数 8 /// </summary> 9 /// <param name="dbCommand"></param> 10 /// <param name="parameters"></param> 11 public static void SetParameters(this IDbCommand dbCommand, IDictionary<string, object> parameters) 12 { 13 if (parameters == null) 14 { 15 return; 16 } 17 foreach (var parameter in parameters) 18 { 19 if (parameter.Value != null) 20 { 21 dbCommand.Parameters.Add(new SqlParameter(parameter.Key, parameter.Value)); 22 } 23 else 24 { 25 dbCommand.Parameters.Add(new SqlParameter(parameter.Key,DBNull.Value)); 26 } 27 } 28 } 29 30 /// <summary> 31 /// 获取对应的实体 32 /// </summary> 33 /// <typeparam name="TEntity"></typeparam> 34 /// <param name="TEntity"></param> 35 /// <returns></returns> 36 public static TEntity GetReaderData<TEntity>(this IDataReader reader) where TEntity : class, new() 37 { 38 var item = new TEntity(); 39 var filedList = new List<string>(); 40 for (int i = 0; i < reader.FieldCount; i++) 41 { 42 filedList.Add(reader.GetName(i)); 43 } 44 //映射数据库中的字段到实体属性 45 IEnumerable<PropertyInfo> propertys = typeof(TEntity).GetProperties().Where(s => filedList.Contains(s.Name)); 46 foreach (var property in propertys) 47 { 48 //对实体属性进行设值 49 property.SetValue(item, reader[property.Name]); 50 } 51 return item; 52 } 53 54 /// <summary> 55 /// 根据列名获取值 56 /// </summary> 57 /// <typeparam name="T"></typeparam> 58 /// <param name="reader"></param> 59 /// <param name="columnName"></param> 60 /// <returns></returns> 61 public static T GetValue<T>(this IDataReader reader, string columnName) 62 { 63 int index = reader.GetOrdinal(columnName); 64 if (reader.IsDBNull(index)) 65 { 66 return default(T); 67 } 68 return (T)reader[index]; 69 } 70 71 /// <summary> 72 /// 获取对应的实体 73 /// </summary> 74 /// <param name="reader"></param> 75 /// <param name="type"></param> 76 /// <returns></returns> 77 public static object GetReaderData(this IDataReader reader,Type type) 78 { 79 var item = Activator.CreateInstance(type); 80 var filedList = new List<string>(); 81 for (int i = 0; i < reader.FieldCount; i++) 82 { 83 filedList.Add(reader.GetName(i).ToLower()); 84 } 85 var properties = (from s in type.GetProperties() 86 let name = s.Name.ToLower().Split(new string[] { "_" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() 87 where filedList.Contains(s.Name) 88 select new 89 { 90 Name = s.Name, 91 Property = s 92 }).ToList(); 93 94 foreach (var property in properties) 95 { 96 property.Property.SetValue(item, reader[property.Name]); 97 } 98 return item; 99 } 100 }
至此,使用Ado.Net 实现的仓储完成啦!当然这只是非泛型版本。当需要使用ORM时,该系列会同步更新泛型版本的实现对聚合类的持久化操作的仓储模式。
相信各位广大园友看到这里,一定不难看出这和DAL层好像没什么区别,没错,对于非泛型的仓储实现等同于DAL层。有人可能会认为这是一种多余的设计,对于未使用ORM的数据存储来说,这里也就只是提供一种额外的设计思路罢了。
在接下来的一篇中将实现数据访问逻辑和领域层完全解耦。