自己动手写ORM(02):Sql生成器实现
Posted i++ˇ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己动手写ORM(02):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 }
那么在生成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 }
具体怎么将实体类转换成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 }
下面是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 }
到此为止,生成的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 }
通过下面工具类生成完整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 }
下面是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 }
下面是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<以上是关于自己动手写ORM(02):Sql生成器实现的主要内容,如果未能解决你的问题,请参考以下文章