SqlTranslateService.cs 9.5 KB

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