回顧: 上一節中鄙人通過解析表達式樹生成Sql碎片,其中我也把解析表達式類代碼貼了出來,文章發佈之後我對ExpressionAnalyzer類做了些改動,下麵我還會將代碼貼出來,廢話不多說,直接進入今天的主題。 實體類設計: 首先,我覺得要想直接通過實體類生成Sql語句,那麼你可能要知道這個實體類對 ...
回顧:
上一節中鄙人通過解析表達式樹生成Sql碎片,其中我也把解析表達式類代碼貼了出來,文章發佈之後我對ExpressionAnalyzer類做了些改動,下麵我還會將代碼貼出來,廢話不多說,直接進入今天的主題。
實體類設計:
首先,我覺得要想直接通過實體類生成Sql語句,那麼你可能要知道這個實體類對應資料庫表中的主鍵和外鍵是什麼,在此我加入了兩個特性來標識主鍵和外鍵關係。如下
/// <summary> /// 外鍵表特性(導航屬性) /// </summary> public class GuidanceAttribute : System.Attribute { /// <summary> /// 依賴欄位 /// </summary> public string DepandField { get; set; } } public class MessageAttribute : System.Attribute { /// <summary> /// 鏈接字元串名 /// </summary> public string ConStr { get; set; } } public class PrimaryKeyAttribute : System.Attribute { }
實體類如下:
1 using LC.Model; 2 using LC.Model.Attribute; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 using LC.Model.Common; 9 using LC.Test; 10 using LC.Test.Model; 11 12 namespace LC.Model 13 { 14 [Message(ConStr = "sql")] 15 public class SYS_User 16 { 17 [PrimaryKey] 18 public string Id { get; set; } 19 public string UserName { get; set; } 20 21 public Guid? RoleId { get; set; } 22 public Guid? CityId { get; set; } 23 [Guidance(DepandField = "RoleId")] 24 public SYS_Role Role { get; set; } 25 26 [Guidance(DepandField = "CityId")] 27 public City City { get; set; } 28 29 public int Gender { get; set; } 30 31 public bool Deleted { get; set; } 32 33 public DateTime CreateTime { get; set; } 34 } 35 }View Code
那麼在生成sql的過程中,我需要知道實體所對應表的主鍵和外鍵關係,此關係我保存在類TableInfo中:
1 public class TableInfo 2 { 3 public string ConStr { get; set; } 4 public string RName { get; set; } 5 public string TableName { get; set; } 6 public string PrimaryKey { get; set; } 7 public Dictionary<string, TableInfo> ForeignRefs { get; set; } 8 }View Code
具體怎麼將實體類轉換成TableInfo,在此我暫時使用反射,不過多解釋,因為暫時還不知道有什麼方法不用反射獲取特性。
有了TableInfo,我們就可以輕鬆的生成Sql了。
查詢命令生成器設計:
下麵上圖,這是我用畫圖工具話的,不要問我為什麼,因為電腦沒有建模工具。。。
直接上代碼:
1 public interface ICreator 2 { 3 SqlDebris Debris { get; set; } 4 } 5 6 public class BaseCreator : ICreator 7 { 8 public SqlDebris Debris { get; set; } 9 public BaseCreator() 10 { 11 Debris = new SqlDebris(); 12 } 13 } 14 public interface IQuery : ICreator 15 { 16 void CreateFrom(AnalysisTable data, TableInfo tableInfo); 17 void CreateSelect(AnalysisData data); 18 void CreateWhere(AnalysisData data); 19 void AppendOrder(AnalysisData data, OrderTypeEnum orderType); 20 void AppendSkip(int count); 21 void AppendTake(int count); 22 void GetCount(); 23 }View Code
下麵是QueryCreator的實現:
1 public class QueryCreator : BaseCreator, IQuery 2 { 3 public virtual void CreateSelect(AnalysisData data) 4 { 5 var primaryTable = data.Table; 6 StringBuilder sb = new StringBuilder(); 7 //預設查詢全部列 8 if (data.StackList.Count <= 0) 9 { 10 sb.AppendFormat("[{0}].*", primaryTable.RName); 11 } 12 else 13 { 14 //查詢部分列 15 for (int i = 0; i < data.StackList.Count; i += 3) 16 { 17 sb.AppendFormat("{0} {1} {2},", data.StackList[i], data.StackList[i + 1], data.StackList[i + 2]); 18 } 19 sb.Remove(sb.Length - 1, 1); 20 } 21 Debris.Select = sb.ToString(); 22 } 23 public virtual void CreateFrom(AnalysisTable anlyTable, TableInfo tableInfo) 24 { 25 if (null == anlyTable) 26 { 27 throw new BusinessException(BusinessRes.TableCanNotBeEmpty); 28 } 29 //預設排序信息 30 if (string.IsNullOrEmpty(Debris.Order)) 31 { 32 Debris.Order = string.Format("[{0}].{1} {2}", anlyTable.RName, tableInfo.PrimaryKey, System.Enum.GetName(typeof(OrderTypeEnum), OrderTypeEnum.ASC)); 33 } 34 StringBuilder sb = new StringBuilder(); 35 sb.AppendFormat("[{0}] AS [{1}]", anlyTable.Name, anlyTable.RName); 36 AppendLeftJoinTables(anlyTable, tableInfo, sb); 37 Debris.From = sb.ToString(); 38 } 39 public virtual void CreateWhere(AnalysisData data) 40 { 41 if (data == null || data.StackList.Count() <= 0) 42 { 43 return; 44 } 45 Debris.Where = string.Join(" ", data.StackList); 46 } 47 public virtual void AppendSkip(int count) 48 { 49 Debris.Skip = count; 50 } 51 public virtual void AppendTake(int count) 52 { 53 Debris.Take = count; 54 } 55 public virtual void AppendOrder(AnalysisData data, OrderTypeEnum orderType) 56 { 57 if (data.StackList.Count <= 0) 58 { 59 return; 60 } 61 var field = data.StackList.First(); 62 StringBuilder sb = new StringBuilder(Debris.Order); 63 if (string.IsNullOrEmpty(Debris.Order)) 64 { 65 sb.AppendFormat("{0} {1}", field, System.Enum.GetName(typeof(OrderTypeEnum), orderType)); 66 Debris.Order = sb.ToString(); 67 return; 68 } 69 sb.AppendFormat(",{0} {1}", field, System.Enum.GetName(typeof(OrderTypeEnum), orderType)); 70 Debris.Order = sb.ToString(); 71 } 72 public void GetCount() 73 { 74 Debris.Select = "COUNT(1)"; 75 } 76 77 private KeyValuePair<string, TableInfo> GetForeignReference(TableInfo tInfo, string rName) 78 { 79 var keyValue = tInfo.ForeignRefs.Where(u => u.Value.RName == rName).FirstOrDefault(); 80 if (string.IsNullOrEmpty(keyValue.Key)) 81 { 82 foreach (var item in tInfo.ForeignRefs) 83 { 84 var foreignTable = GetForeignReference(item.Value, rName); 85 if (!string.IsNullOrEmpty(keyValue.Key)) 86 { 87 return foreignTable; 88 } 89 } 90 91 } 92 93 return keyValue; 94 } 95 private void AppendLeftJoinTables(AnalysisTable anlyTable, TableInfo tableInfo, StringBuilder sb) 96 { 97 ///添加關係表信息 98 foreach (var item in anlyTable.leftJoins) 99 { 100 var _foreignRef = GetForeignReference(tableInfo, item.RName); 101 if (string.IsNullOrEmpty(_foreignRef.Key)) 102 { 103 throw new BusinessException(BusinessRes.WhitoutThisForeignReference); 104 } 105 sb.AppendFormat(" LEFT JOIN [{0}] AS [{1}] ON [{1}].{2}=[{3}].{4} ", item.Name, item.RName, _foreignRef.Value.PrimaryKey, anlyTable.RName, _foreignRef.Key); 106 AppendLeftJoinTables(item, _foreignRef.Value, sb); 107 } 108 } 109 }View Code
到此為止,生成的Sql依然是不完整的Sql碎片,我們需要將碎片合併成Sql語句,我寫了一個工具類如下:
保存碎片類:
1 public class SqlDebris 2 { 3 public string Where { get; set; } 4 #region 查詢 5 public string From { get; set; } 6 public string Select { get; set; } 7 public string Order { get; set; } 8 public int Skip { get; set; } 9 public int? Take { get; set; } 10 #endregion 11 #region 修改 12 public string Set { get; set; } 13 public string Update { get; set; } 14 #endregion 15 #region 新增 16 17 public string InsertTable { get; set; } 18 public string Columns { get; set; } 19 public string Values { get; set; } 20 21 #endregion 22 }View Code
通過下麵工具類生成完整Sql語句:
1 public class CommandTextHelper 2 { 3 public static string GetSelectCommandText(ICreator creator) 4 { 5 var debris = creator.Debris; 6 StringBuilder sb = new StringBuilder(); 7 if (debris.Take == null) 8 { 9 sb.Append(" SELECT * FROM ("); 10 } 11 else 12 { 13 //分頁 14 sb.AppendFormat(" SELECT TOP {0} * FROM (", debris.Take); 15 } 16 sb.AppendFormat("SELECT {0},ROW_NUMBER() OVER(ORDER BY {2} ) as ROWNUMBER FROM {1}", debris.Select, debris.From, debris.Order); 17 //條件 18 if (!string.IsNullOrEmpty(debris.Where)) 19 { 20 sb.AppendFormat(" WHERE {0} ", debris.Where); 21 } 22 sb.Append(") as NEW_TABLE "); 23 sb.AppendFormat(" WHERE ROWNUMBER>{0}", debris.Skip); 24 return sb.ToString(); 25 } 26 27 public static string GetCountCommandText(ICreator creator) 28 { 29 var debris = creator.Debris; 30 StringBuilder sb = new StringBuilder(); 31 32 sb.AppendFormat("SELECT {0} FROM {1}", debris.Select, debris.From); 33 //條件 34 if (!string.IsNullOrEmpty(debris.Where)) 35 { 36 sb.AppendFormat(" WHERE {0} ", debris.Where); 37 } 38 return sb.ToString(); 39 } 40 41 public static string GetUpdateCommandText(ICreator creator) 42 { 43 var debris = creator.Debris; 44 StringBuilder sb = new StringBuilder(); 45 sb.AppendFormat("UPDATE {0} ", debris.Update); 46 sb.AppendFormat("SET {0} ", debris.Set); 47 sb.AppendFormat("WHERE {0}", debris.Where); 48 return sb.ToString(); 49 } 50 public static string GetInsertCommandText(ICreator creator) 51 { 52 var debris = creator.Debris; 53 StringBuilder sb = new StringBuilder(); 54 sb.AppendFormat("INSERT INTO {0}", debris.InsertTable); 55 sb.AppendFormat("({0}) ", debris.Columns); 56 sb.AppendFormat("VALUES({0})", debris.Values); 57 return sb.ToString(); 58 } 59 public static string GetDeleteCommandText(ICreator creator) 60 { 61 var debris = creator.Debris; 62 StringBuilder sb = new StringBuilder(); 63 sb.AppendFormat("DELETE FROM {0}", debris.From); 64 sb.AppendFormat(" WHERE {0}", debris.Where); 65 return sb.ToString(); 66 } 67 }View Code
下麵是CommandFactory<TEntity>:
1 public class CommandFactory<TEntity> 2 { 3 protected TableInfo _tableInfo; 4 public CommandFactory() 5 { 6 _tableInfo = new Mapper().MapToTable(typeof(TEntity)); 7 } 8 }View Code
下麵是QueryCommand<TEntity>實現:
1 public class QueryCommand<TEntity> : CommandFactory<TEntity> 2 { 3 //sql碎片生成器 4 private IQuery _creator; 5 //查詢參數 6 private Dictionary<string, object> _params; 7 8 private AnalysisTable _table; 9 10 public QueryCommand() 11 { 12 _creator = new QueryCreator(); 13 } 14 public QueryCommand<TEntity> Where(Expression<Func<TEntity, bool>> exp) 15 { 16 var retData = new ExpressionAnalyzer(exp).GetAnalysisResult(); 17 _creator.CreateWhere(retData); 18 _params = retData.ParamList; 19 _table = retData.Table; 20 return this; 21 } 22 public QueryCommand<TEntity> OrderBy(Expression<Func<TEntity, object>> exp) 23 { 24 _creator.AppendOrder(new ExpressionAnalyzer(exp).GetAnalysisResult(), OrderTypeEnum.ASC); 25 return this; 26 } 27 public QueryCommand<TEntity> OrderByDescding(Expression<Func<TEntity, object>> exp) 28 { 29 _creator.AppendOrder(new ExpressionAnalyzer(exp).GetAnalysisResult(), OrderTypeEnum.DESC); 30 return this; 31 } 32 public QueryCommand<TEntity> ThenBy(Expression<Func<TEntity, object>> exp) 33 { 34 return OrderBy(exp); 35 } 36 public QueryCommand<TEntity> ThenByDescding(Expression<Func<TEntity, object>> exp) 37 { 38 return OrderByDescding(exp); 39 } 40 public QueryCommand<TEntity> Skip(int count) 41 { 42 _creator.AppendSkip(count); 43 return this; 44 } 45 public QueryCommand<TEntity> Take(int count) 46 { 47 _creator.AppendTake(count); 48 return this; 49 } 50 public Command<TResult> GetSelectCommand<TResult>(Expression<Func<TEntity, TResult>> exp) 51 { 52 var _result = new ExpressionAnalyzer(exp, _table).GetAnalysisResult(); 53 _creator.CreateSelect(_result); 54 _creator.CreateFrom(_result.Table, _tableInfo); 55 return new Command<TResult>() 56 { 57 ConStr = _tableInfo.ConStr, 58 CommandText = CommandTextHelper.GetSelectCommandText(_creator), 59 Params = _params 60 }; 61 } 62 public Command<int> GetCountCommand(Expression<Func<TEntity, bool>> exp) 63 { 64 var retData = new ExpressionAnalyzer(exp).GetAnalysisResult(); 65 _creator.CreateWhere(retData); 66 _creator.GetCount(); 67 _creator.CreateFrom(retData.Table, _tableInfo); 68 return new Command<int>() 69 { 70 CommandText = CommandTextHelper.GetCountCommandText(_creator), 71 ConStr = _tableInfo.ConStr, 72 Params = retData.ParamList 73 }; 74 } 75 }View Code
下麵是測試結果僅供參考:
1 [TestMethod] 2 public void TestQueryCommand() 3 { 4 var param = new SYS_User() 5 { 6 UserName = "lichun", 7 RoleId = Guid.NewGuid() 8 }; 9 QueryCommand<SYS_User> f = new QueryCommand<SYS_User>(); 10 var command = f.Where(u => u.City.Name == "cengdu" && u.Role.Config.Name.Contains(param.UserName)).GetSelectCommand(u => new 11 { 12 u.Id, 13 u.Role.Name 14 }); 15 ShowCommand(command); 16 } 17 18 19 public void ShowCommand<TResult>(Command<TResult> command) 20 { 21 Console.WriteLine("鏈接字元串:"); 22 Console.WriteLine(command.ConStr); 23 Console.WriteLine("----------------------------------");
Console.WriteLine("Sql命令:"); 24 Console.WriteLine(command.CommandText); 25 26 Console.WriteLine("----------------------------------