SqlTranslateService.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations.Schema;
  4. using System.Linq.Expressions;
  5. using Vit.Extensions.Linq_Extensions;
  6. using Vit.Linq.ExpressionTree.ComponentModel;
  7. using Vitorm.Entity;
  8. using Vitorm.Entity.Dapper;
  9. using Vitorm.Sql;
  10. using Vitorm.Sql.SqlTranslate;
  11. using Vitorm.SqlServer.TranslateService;
  12. using Vitorm.StreamQuery;
  13. namespace Vitorm.SqlServer
  14. {
  15. public class SqlTranslateService : Vitorm.Sql.SqlTranslate.SqlTranslateService
  16. {
  17. public static readonly SqlTranslateService Instance = new SqlTranslateService();
  18. protected Vitorm.SqlServer.SqlTranslate.QueryTranslateService queryTranslateService;
  19. protected ExecuteUpdateTranslateService executeUpdateTranslateService;
  20. protected ExecuteDeleteTranslateService executeDeleteTranslateService;
  21. public SqlTranslateService()
  22. {
  23. queryTranslateService = new(this);
  24. executeUpdateTranslateService = new ExecuteUpdateTranslateService(this);
  25. executeDeleteTranslateService = new ExecuteDeleteTranslateService(this);
  26. }
  27. /// <summary>
  28. /// Generates the delimited SQL representation of an identifier (column name, table name, etc.).
  29. /// </summary>
  30. /// <param name="identifier">The identifier to delimit.</param>
  31. /// <returns>
  32. /// The generated string.
  33. /// </returns>
  34. public override string DelimitIdentifier(string identifier) => $"[{EscapeIdentifier(identifier)}]"; // Interpolation okay; strings
  35. /// <summary>
  36. /// Generates the escaped SQL representation of an identifier (column name, table name, etc.).
  37. /// </summary>
  38. /// <param name="identifier">The identifier to be escaped.</param>
  39. /// <returns>
  40. /// The generated string.
  41. /// </returns>
  42. public override string EscapeIdentifier(string identifier) => identifier.Replace("[", "\"[").Replace("]", "\"]");
  43. public override string DelimitTableName(IEntityDescriptor entityDescriptor)
  44. {
  45. if (entityDescriptor.schema == null) return DelimitIdentifier(entityDescriptor.tableName);
  46. return $"{DelimitIdentifier(entityDescriptor.schema)}.{DelimitIdentifier(entityDescriptor.tableName)}";
  47. }
  48. #region EvalExpression
  49. /// <summary>
  50. /// read where or value or on
  51. /// </summary>
  52. /// <param name="arg"></param>
  53. /// <returns></returns>
  54. /// <exception cref="NotSupportedException"></exception>
  55. /// <param name="data"></param>
  56. public override string EvalExpression(QueryTranslateArgument arg, ExpressionNode data)
  57. {
  58. switch (data.nodeType)
  59. {
  60. case NodeType.MethodCall:
  61. {
  62. ExpressionNode_MethodCall methodCall = data;
  63. switch (methodCall.methodName)
  64. {
  65. // ##1 ToString
  66. case nameof(object.ToString):
  67. {
  68. return $"cast({EvalExpression(arg, methodCall.@object)} as varchar(max))";
  69. }
  70. #region ##2 String method: StartsWith EndsWith Contains
  71. case nameof(string.StartsWith): // String.StartsWith
  72. {
  73. var str = methodCall.@object;
  74. var value = methodCall.arguments[0];
  75. return $"{EvalExpression(arg, str)} like {EvalExpression(arg, value)}+'%'";
  76. }
  77. case nameof(string.EndsWith): // String.EndsWith
  78. {
  79. var str = methodCall.@object;
  80. var value = methodCall.arguments[0];
  81. return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}";
  82. }
  83. case nameof(string.Contains) when methodCall.methodCall_typeName == "String": // String.Contains
  84. {
  85. var str = methodCall.@object;
  86. var value = methodCall.arguments[0];
  87. return $"{EvalExpression(arg, str)} like '%'+{EvalExpression(arg, value)}+'%'";
  88. }
  89. #endregion
  90. }
  91. break;
  92. }
  93. #region Read Value
  94. case NodeType.Convert:
  95. {
  96. // cast( 4.1 as signed)
  97. ExpressionNode_Convert convert = data;
  98. Type targetType = convert.valueType?.ToType();
  99. if (targetType == typeof(object)) return EvalExpression(arg, convert.body);
  100. // Nullable
  101. if (targetType.IsGenericType) targetType = targetType.GetGenericArguments()[0];
  102. string targetDbType = GetDbType(targetType);
  103. var sourceType = convert.body.Member_GetType();
  104. if (sourceType != null)
  105. {
  106. if (sourceType.IsGenericType) sourceType = sourceType.GetGenericArguments()[0];
  107. if (targetDbType == GetDbType(sourceType)) return EvalExpression(arg, convert.body);
  108. }
  109. return $"cast({EvalExpression(arg, convert.body)} as {targetDbType})";
  110. }
  111. case nameof(ExpressionType.Add):
  112. {
  113. ExpressionNode_Binary binary = data;
  114. // ##1 String Add
  115. if (data.valueType?.ToType() == typeof(string))
  116. {
  117. return $"CONCAT({EvalExpression(arg, binary.left)} ,{EvalExpression(arg, binary.right)})";
  118. }
  119. // ##2 Numberic Add
  120. return $"{EvalExpression(arg, binary.left)} + {EvalExpression(arg, binary.right)}";
  121. }
  122. case nameof(ExpressionType.Coalesce):
  123. {
  124. ExpressionNode_Binary binary = data;
  125. return $"COALESCE({EvalExpression(arg, binary.left)},{EvalExpression(arg, binary.right)})";
  126. }
  127. #endregion
  128. }
  129. return base.EvalExpression(arg, data);
  130. }
  131. #endregion
  132. #region PrepareCreate
  133. public override string PrepareCreate(IEntityDescriptor entityDescriptor)
  134. {
  135. /* //sql
  136. CREATE TABLE user (
  137. id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
  138. name varchar(100) DEFAULT NULL,
  139. birth date DEFAULT NULL,
  140. fatherId int DEFAULT NULL,
  141. motherId int DEFAULT NULL
  142. ) ;
  143. */
  144. List<string> sqlFields = new();
  145. // #1 primary key
  146. sqlFields.Add(GetColumnSql(entityDescriptor.key) + " " + (entityDescriptor.key.databaseGenerated == DatabaseGeneratedOption.Identity ? "PRIMARY KEY IDENTITY(1,1) " : ""));
  147. // #2 columns
  148. entityDescriptor.columns?.ForEach(column => sqlFields.Add(GetColumnSql(column)));
  149. return $@"
  150. CREATE TABLE {DelimitTableName(entityDescriptor)} (
  151. {string.Join(",\r\n ", sqlFields)}
  152. )";
  153. string GetColumnSql(IColumnDescriptor column)
  154. {
  155. var dbType = column.databaseType ?? GetDbType(column.type);
  156. // name varchar(100) DEFAULT NULL
  157. return $" {DelimitIdentifier(column.name)} {dbType} {(column.nullable ? "DEFAULT NULL" : "NOT NULL")}";
  158. }
  159. }
  160. protected readonly static Dictionary<Type, string> dbTypeMap = new()
  161. {
  162. [typeof(DateTime)] = "datetime",
  163. [typeof(string)] = "varchar(max)",
  164. [typeof(float)] = "float",
  165. [typeof(double)] = "float",
  166. [typeof(decimal)] = "float",
  167. [typeof(Int32)] = "int",
  168. [typeof(Int16)] = "smallint",
  169. [typeof(byte)] = "tinyint",
  170. [typeof(bool)] = "bit",
  171. };
  172. protected override string GetDbType(Type type)
  173. {
  174. type = TypeUtil.GetUnderlyingType(type);
  175. if (dbTypeMap.TryGetValue(type, out var dbType)) return dbType;
  176. throw new NotSupportedException("unsupported column type:" + type.Name);
  177. }
  178. #endregion
  179. public override (string sql, Func<object, Dictionary<string, object>> GetSqlParams) PrepareAdd(SqlTranslateArgument arg)
  180. {
  181. var result = base.PrepareAdd(arg);
  182. // get generated id
  183. result.sql += "select convert(int,isnull(SCOPE_IDENTITY(),-1));";
  184. return result;
  185. }
  186. public override (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) PrepareQuery(QueryTranslateArgument arg, CombinedStream combinedStream)
  187. {
  188. string sql = queryTranslateService.BuildQuery(arg, combinedStream);
  189. return (sql, arg.sqlParam, arg.dataReader);
  190. }
  191. public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteUpdate(QueryTranslateArgument arg, CombinedStream combinedStream)
  192. {
  193. string sql = executeUpdateTranslateService.BuildQuery(arg, combinedStream);
  194. return (sql, arg.sqlParam);
  195. }
  196. public override (string sql, Dictionary<string, object> sqlParam) PrepareExecuteDelete(QueryTranslateArgument arg, CombinedStream combinedStream)
  197. {
  198. string sql = executeDeleteTranslateService.BuildQuery(arg, combinedStream);
  199. return (sql, arg.sqlParam);
  200. }
  201. }
  202. }