經過前3章文章,大體對ORM有必定的瞭解,但也存在效率低下(大量用了反射)和重複代碼,今天咱們要對ORM進行優化。sql
具體流程以下:數據庫
咱們優化的第一個就是減小反射調用,個人思路是定義一個Mapping,把表名、字段名信息緩存起來,EntityMapping 表示實體類信息對應數據庫中的table,MemberMapping表示實體類的屬性對應數據庫中的Column。緩存
EntityMapping代碼:app
class EntityMapping { /// <summary> /// 對應實體類類型 /// </summary> public Type EntityType { get; internal set; } /// <summary> /// 表名 /// </summary> public string TableName { get; set; } /// <summary> /// 實體屬性 /// </summary> public List<MemberMapping> Members; /// <summary> /// 主鍵 /// </summary> public List<MemberMapping> PrimaryKey { get; set; } }
MemberMapping代碼:ide
class MemberMapping { /// <summary> /// 列名 /// </summary> public string ColumnName { get; internal set; } /// <summary> /// 屬性名 /// </summary> public PropertyInfo Member { get; set; } /// <summary> /// 是否爲主鍵 /// </summary> public bool IsPrimaryKey { get; set; } /// <summary> /// 是否爲數據庫自動生成 /// </summary> public bool IsDbGenerated { get; set; } }
有Mapping後,咱們如今要作的是把實體類裏的信息存到Mapping類中,我在這裏定義了一個AttributeMapping,裏面方法以下:優化
class AttributeMapping { private static Dictionary<Type, EntityMapping> entityMappings = new Dictionary<Type, EntityMapping>(); public static EntityMapping Get<T>() public static EntityMapping Get(Type type) private static EntityMapping CreateMapping(Type type) }
其中Get方法就是根據實體類返回Mapping信息,而CreateMapping則是建立Mapping,具體代碼見附件下載。ui
Mapping信息有了,咱們須要作的是把重構以前的EntityHelper類,而這個類裏有大量的連接數據庫、操做數據庫代碼,咱們把這些信息抽離到一個單獨類,就比如咱們經常使用的DbHelper類,而這個類我命名爲DbProvider。this
class DbProvider { private IDbConnection conntion; public DbProvider(IDbConnection conntion) public virtual int ExecuteNonQuery(string sql, Dictionary<string, object> parameters) public virtual IDataReader ExecuteReader(string sql, Dictionary<string, object> parameters) }
EntityHelper類還有大量的SQL生成語句,咱們爲了簡化EntityHelper類把SQL生成的代碼放到單獨的DbSqlBuilder類,爲啥要這麼作呢?除了簡化代碼外,還有就是爲了適應不一樣的Db。spa
class DbSqlBuilder { public string BuildInsertSql(EntityMapping entityMapping, List<string> columnNames) public string BuildUpdateSql(EntityMapping entityMapping, List<string> updateColumns,List<string> whereColumns) public string BuildDeleteSql(EntityMapping entityMapping, List<string> whereColumns) public string BuildSelectSql(EntityMapping entityMapping,string strWhere, string orderBy) }
爲何參數裏要傳遞List<string>呢?由於咱們拼SQL語句要用到列名的,修改後的EntityHelper(我在這裏將名字改爲DbContext)。code
將數據庫操做和SQL生成代碼放到單獨類裏,回頭再看下DbContext代碼,比原來的簡潔了不少。
class DbContext { private DbProvider dbProvider; private DbSqlBuilder sqlBuilder ; public DbContext(string connectionString) { SqlConnection conn = new SqlConnection(connectionString); dbProvider = new DbProvider(conn); this.sqlBuilder = new DbSqlBuilder(); } public int Insert<T>(object entity) { var entityMapping = AttributeMapping.Get<T>(); //將Entity轉換成Dictionary var parameters = DynamicMethodBuilder.ConvertFromObject(entity); var sql = sqlBuilder.BuildInsertSql(entityMapping, parameters.Keys.ToList()); return dbProvider.ExecuteNonQuery(sql, parameters); } public int Update<T>(T entity) { var entityMapping = AttributeMapping.Get<T>(); //將Entity轉換成Dictionary var parameters = DynamicMethodBuilder.ConvertFromObject(entity); var columns = entityMapping.Members.Where(m => m.IsDbGenerated == false && m.IsPrimaryKey == false).Select(c => c.ColumnName).ToArray(); var updateColumns = new Dictionary<string, object>(); var whereColumns = new Dictionary<string, object>(); foreach (var item in parameters) { if (columns.Contains(item.Key)) updateColumns.Add(item.Key, item.Value); if (entityMapping.PrimaryKey.All(m => m.ColumnName == item.Key)) whereColumns.Add(item.Key, item.Value); } var sql = sqlBuilder.BuildUpdateSql(entityMapping, updateColumns.Keys.ToList(), whereColumns.Keys.ToList()); return dbProvider.ExecuteNonQuery(sql, parameters); } public int DeleteByKey<T>(params object[] values) { var entityMapping = AttributeMapping.Get<T>(); if (values.Length != entityMapping.PrimaryKey.Count) throw new ArgumentException("參數個數和主鍵數不一致"); var parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < entityMapping.PrimaryKey.Count; i++) { parameters.Add(entityMapping.PrimaryKey[i].ColumnName, values[i]); } var sql = sqlBuilder.BuildDeleteSql(entityMapping, parameters.Keys.ToList()); return dbProvider.ExecuteNonQuery(sql, parameters); } public T Get<T>(object[] values) { var entityMapping = AttributeMapping.Get<T>(); if (values.Length != entityMapping.PrimaryKey.Count) throw new ArgumentException("參數個數和主鍵數不一致"); var parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); StringBuilder where = new StringBuilder(); for (int i = 0; i < values.Length; i++) { if (i > 0) //考慮到有多個主鍵 where.Append(" AND "); where.Append(entityMapping.PrimaryKey[i].ColumnName).Append("=").Append("@p").Append(i); /*參數*/ parameters.Add("@p" + i, values[i]); } var sql = this.sqlBuilder.BuildSelectSql(entityMapping, where.ToString(), string.Empty); return GetEntityList<T>(sql, parameters).FirstOrDefault(); } public List<T> GetList<T>(string where, string orderBy) { var entityMapping = AttributeMapping.Get<T>(); var sql = this.sqlBuilder.BuildSelectSql(entityMapping, where.ToString(), orderBy); return GetEntityList<T>(sql, null); } public List<T> GetEntityList<T>(string sql, Dictionary<string, object> parameters) { var reader = dbProvider.ExecuteReader(sql, parameters); 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; } }
本章結束,下一章要將DataReader轉換成實體類的代碼優化。