这是我在博客园的第一遍文章,想分享下lambda表达式转换sql。
喜欢EF的便捷与优雅,不喜欢生成的一坨sql。(PS:公司封装了一套访问数据库的方法,所以不确定是不是EF的问题,反正就是一坨密密麻麻的的sql,我有点点处女座小纠结,虽然我是天蝎座)
好了,废话少说。
1 public class LambdaToSqlHelper 2 { 3 /// <summary> 4 /// NodeType枚举 5 /// </summary> 6 private enum EnumNodeType 7 { 8 /// <summary> 9 /// 二元运算符 10 /// </summary> 11 [Description("二元运算符")] 12 BinaryOperator = 1, 13 14 /// <summary> 15 /// 一元运算符 16 /// </summary> 17 [Description("一元运算符")] 18 UndryOperator = 2, 19 20 /// <summary> 21 /// 常量表达式 22 /// </summary> 23 [Description("常量表达式")] 24 Constant = 3, 25 26 /// <summary> 27 /// 成员(变量) 28 /// </summary> 29 [Description("成员(变量)")] 30 MemberAccess = 4, 31 32 /// <summary> 33 /// 函数 34 /// </summary> 35 [Description("函数")] 36 Call = 5, 37 38 /// <summary> 39 /// 未知 40 /// </summary> 41 [Description("未知")] 42 Unknown = -99, 43 44 /// <summary> 45 /// 不支持 46 /// </summary> 47 [Description("不支持")] 48 NotSupported = -98 49 } 50 51 /// <summary> 52 /// 判断表达式类型 53 /// </summary> 54 /// <param name="exp">lambda表达式</param> 55 /// <returns></returns> 56 private static EnumNodeType CheckExpressionType(Expression exp) 57 { 58 switch (exp.NodeType) 59 { 60 case ExpressionType.AndAlso: 61 case ExpressionType.OrElse: 62 case ExpressionType.Equal: 63 case ExpressionType.GreaterThanOrEqual: 64 case ExpressionType.LessThanOrEqual: 65 case ExpressionType.GreaterThan: 66 case ExpressionType.LessThan: 67 case ExpressionType.NotEqual: 68 return EnumNodeType.BinaryOperator; 69 case ExpressionType.Constant: 70 return EnumNodeType.Constant; 71 case ExpressionType.MemberAccess: 72 return EnumNodeType.MemberAccess; 73 case ExpressionType.Call: 74 return EnumNodeType.Call; 75 case ExpressionType.Not: 76 case ExpressionType.Convert: 77 return EnumNodeType.UndryOperator; 78 default: 79 return EnumNodeType.Unknown; 80 } 81 } 82 83 /// <summary> 84 /// 表达式类型转换 85 /// </summary> 86 /// <param name="type"></param> 87 /// <returns></returns> 88 private static string ExpressionTypeCast(ExpressionType type) 89 { 90 switch (type) 91 { 92 case ExpressionType.And: 93 case ExpressionType.AndAlso: 94 return " and "; 95 case ExpressionType.Equal: 96 return " = "; 97 case ExpressionType.GreaterThan: 98 return " > "; 99 case ExpressionType.GreaterThanOrEqual: 100 return " >= "; 101 case ExpressionType.LessThan: 102 return " < "; 103 case ExpressionType.LessThanOrEqual: 104 return " <= "; 105 case ExpressionType.NotEqual: 106 return " <> "; 107 case ExpressionType.Or: 108 case ExpressionType.OrElse: 109 return " or "; 110 case ExpressionType.Add: 111 case ExpressionType.AddChecked: 112 return " + "; 113 case ExpressionType.Subtract: 114 case ExpressionType.SubtractChecked: 115 return " - "; 116 case ExpressionType.Divide: 117 return " / "; 118 case ExpressionType.Multiply: 119 case ExpressionType.MultiplyChecked: 120 return " * "; 121 default: 122 return null; 123 } 124 } 125 126 private static string BinarExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 127 { 128 BinaryExpression be = exp as BinaryExpression; 129 Expression left = be.Left; 130 Expression right = be.Right; 131 ExpressionType type = be.NodeType; 132 string sb = "("; 133 //先处理左边 134 sb += ExpressionRouter(left, listSqlParaModel); 135 sb += ExpressionTypeCast(type); 136 //再处理右边 137 string sbTmp = ExpressionRouter(right, listSqlParaModel); 138 if (sbTmp == "null") 139 { 140 if (sb.EndsWith(" = ")) 141 sb = sb.Substring(0, sb.Length - 2) + " is null"; 142 else if (sb.EndsWith(" <> ")) 143 sb = sb.Substring(0, sb.Length - 2) + " is not null"; 144 } 145 else 146 sb += sbTmp; 147 return sb += ")"; 148 } 149 150 private static string ConstantExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 151 { 152 ConstantExpression ce = exp as ConstantExpression; 153 if (ce.Value == null) 154 { 155 return "null"; 156 } 157 else if (ce.Value is ValueType) 158 { 159 GetSqlParaModel(listSqlParaModel, GetValueType(ce.Value)); 160 return "@para" + listSqlParaModel.Count; 161 } 162 else if (ce.Value is string || ce.Value is DateTime || ce.Value is char) 163 { 164 GetSqlParaModel(listSqlParaModel, GetValueType(ce.Value)); 165 return "@para" + listSqlParaModel.Count; 166 } 167 return ""; 168 } 169 170 private static string LambdaExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 171 { 172 LambdaExpression le = exp as LambdaExpression; 173 return ExpressionRouter(le.Body, listSqlParaModel); 174 } 175 176 private static string MemberExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 177 { 178 if (!exp.ToString().StartsWith("value")) 179 { 180 MemberExpression me = exp as MemberExpression; 181 if (me.Member.Name == "Now") 182 { 183 GetSqlParaModel(listSqlParaModel, DateTime.Now); 184 return "@para" + listSqlParaModel.Count; 185 } 186 return me.Member.Name; 187 } 188 else 189 { 190 var result = Expression.Lambda(exp).Compile().DynamicInvoke(); 191 if (result == null) 192 { 193 return "null"; 194 } 195 else if (result is ValueType) 196 { 197 GetSqlParaModel(listSqlParaModel, GetValueType(result)); 198 return "@para" + listSqlParaModel.Count; 199 } 200 else if (result is string || result is DateTime || result is char) 201 { 202 GetSqlParaModel(listSqlParaModel, GetValueType(result)); 203 return "@para" + listSqlParaModel.Count; 204 } 205 else if (result is int[]) 206 { 207 var rl = result as int[]; 208 StringBuilder sbTmp = new StringBuilder(); 209 foreach (var r in rl) 210 { 211 GetSqlParaModel(listSqlParaModel, r.ToString().ToInt32()); 212 sbTmp.Append("@para" + listSqlParaModel.Count + ","); 213 } 214 return sbTmp.ToString().Substring(0, sbTmp.ToString().Length - 1); 215 } 216 else if (result is string[]) 217 { 218 var rl = result as string[]; 219 StringBuilder sbTmp = new StringBuilder(); 220 foreach (var r in rl) 221 { 222 GetSqlParaModel(listSqlParaModel, r.ToString()); 223 sbTmp.Append("@para" + listSqlParaModel.Count + ","); 224 } 225 return sbTmp.ToString().Substring(0, sbTmp.ToString().Length - 1); 226 } 227 } 228 return ""; 229 } 230 231 private static string MethodCallExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 232 { 233 MethodCallExpression mce = exp as MethodCallExpression; 234 if (mce.Method.Name == "Contains") 235 { 236 if (mce.Object == null) 237 { 238 return string.Format("{0} in ({1})", ExpressionRouter(mce.Arguments[1], listSqlParaModel), ExpressionRouter(mce.Arguments[0], listSqlParaModel)); 239 } 240 else 241 { 242 if (mce.Object.NodeType == ExpressionType.MemberAccess) 243 { 244 //w => w.name.Contains("1") 245 var _name = ExpressionRouter(mce.Object, listSqlParaModel); 246 var _value = ExpressionRouter(mce.Arguments[0], listSqlParaModel); 247 var index = _value.RetainNumber().ToInt32() - 1; 248 listSqlParaModel[index].value = "%{0}%".FormatWith(listSqlParaModel[index].value); 249 return string.Format("{0} like {1}", _name, _value); 250 } 251 } 252 } 253 else if (mce.Method.Name == "OrderBy") 254 { 255 return string.Format("{0} asc", ExpressionRouter(mce.Arguments[1], listSqlParaModel)); 256 } 257 else if (mce.Method.Name == "OrderByDescending") 258 { 259 return string.Format("{0} desc", ExpressionRouter(mce.Arguments[1], listSqlParaModel)); 260 } 261 else if (mce.Method.Name == "ThenBy") 262 { 263 return string.Format("{0},{1} asc", MethodCallExpressionProvider(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel)); 264 } 265 else if (mce.Method.Name == "ThenByDescending") 266 { 267 return string.Format("{0},{1} desc", MethodCallExpressionProvider(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel)); 268 } 269 else if (mce.Method.Name == "Like") 270 { 271 return string.Format("({0} like {1})", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel).Replace("'", "")); 272 } 273 else if (mce.Method.Name == "NotLike") 274 { 275 return string.Format("({0} not like '%{1}%')", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel).Replace("'", "")); 276 } 277 else if (mce.Method.Name == "In") 278 { 279 return string.Format("{0} in ({1})", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel)); 280 } 281 else if (mce.Method.Name == "NotIn") 282 { 283 return string.Format("{0} not in ({1})", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel)); 284 } 285 return ""; 286 } 287 288 private static string NewArrayExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 289 { 290 NewArrayExpression ae = exp as NewArrayExpression; 291 StringBuilder sbTmp = new StringBuilder(); 292 foreach (Expression ex in ae.Expressions) 293 { 294 sbTmp.Append(ExpressionRouter(ex, listSqlParaModel)); 295 sbTmp.Append(","); 296 } 297 return sbTmp.ToString(0, sbTmp.Length - 1); 298 } 299 300 private static string ParameterExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 301 { 302 ParameterExpression pe = exp as ParameterExpression; 303 return pe.Type.Name; 304 } 305 306 private static string UnaryExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel) 307 { 308 UnaryExpression ue = exp as UnaryExpression; 309 var result = ExpressionRouter(ue.Operand, listSqlParaModel); 310 ExpressionType type = exp.NodeType; 311 if (type == ExpressionType.Not) 312 { 313 if (result.Contains(" in ")) 314 { 315 result = result.Replace(" in ", " not in "); 316 } 317 if (result.Contains(" like ")) 318 { 319 result = result.Replace(" like ", " not like "); 320 } 321 } 322 return result; 323 } 324 325 /// <summary> 326 /// 路由计算 327 /// </summary> 328 /// <param name="exp"></param> 329 /// <param name="listSqlParaModel"></param> 330 /// <returns></returns> 331 private static string ExpressionRouter(Expression exp, List<SqlParaModel> listSqlParaModel) 332 { 333 var nodeType = exp.NodeType; 334 if (exp is BinaryExpression) //表示具有二进制运算符的表达式 335 { 336 return BinarExpressionProvider(exp, listSqlParaModel); 337 } 338 else if (exp is ConstantExpression) //表示具有常数值的表达式 339 { 340 return ConstantExpressionProvider(exp, listSqlParaModel); 341 } 342 else if (exp is LambdaExpression) //介绍 lambda 表达式。 它捕获一个类似于 .NET 方法主体的代码块 343 { 344 return LambdaExpressionProvider(exp, listSqlParaModel); 345 } 346 else if (exp is MemberExpression) //表示访问字段或属性 347 { 348 return MemberExpressionProvider(exp, listSqlParaModel); 349 } 350 else if (exp is MethodCallExpression) //表示对静态方法或实例方法的调用 351 { 352 return MethodCallExpressionProvider(exp, listSqlParaModel); 353 } 354 else if (exp is NewArrayExpression) //表示创建一个新数组,并可能初始化该新数组的元素 355 { 356 return NewArrayExpressionProvider(exp, listSqlParaModel); 357 } 358 else if (exp is ParameterExpression) //表示一个命名的参数表达式。 359 { 360 return ParameterExpressionProvider(exp, listSqlParaModel); 361 } 362 else if (exp is UnaryExpression) //表示具有一元运算符的表达式 363 { 364 return UnaryExpressionProvider(exp, listSqlParaModel); 365 } 366 return null; 367 } 368 369 /// <summary> 370 /// 值类型转换 371 /// </summary> 372 /// <param name="_value"></param> 373 /// <returns></returns> 374 private static object GetValueType(object _value) 375 { 376 var _type = _value.GetType().Name; 377 switch (_type) 378 { 379 case "Decimal ": return _value.ToDecimal(); 380 case "Int32": return _value.ToInt32(); 381 case "DateTime": return _value.ToDateTime(); 382 case "String": return _value.ToString(); 383 case "Char":return _value.ToChar(); 384 case "Boolean":return _value.ToBoolean(); 385 default: return _value; 386 } 387 } 388 389 /// <summary> 390 /// sql参数 391 /// </summary> 392 /// <param name="listSqlParaModel"></param> 393 /// <param name="val"></param> 394 private static void GetSqlParaModel(List<SqlParaModel> listSqlParaModel, object val) 395 { 396 SqlParaModel p = new SqlParaModel(); 397 p.name = "para" + (listSqlParaModel.Count + 1); 398 p.value = val; 399 listSqlParaModel.Add(p); 400 } 401 402 /// <summary> 403 /// lambda表达式转换sql 404 /// </summary> 405 /// <typeparam name="T"></typeparam> 406 /// <param name="where"></param> 407 /// <param name="listSqlParaModel"></param> 408 /// <returns></returns> 409 public static string GetWhereSql<T>(Expression<Func<T, bool>> where, List<SqlParaModel> listSqlParaModel) where T : class 410 { 411 string result = string.Empty; 412 if (where != null) 413 { 414 Expression exp = where.Body as Expression; 415 result = ExpressionRouter(exp, listSqlParaModel); 416 } 417 if (result != string.Empty) 418 { 419 result = " where " + result; 420 } 421 return result; 422 } 423 424 /// <summary> 425 /// lambda表达式转换sql 426 /// </summary> 427 /// <typeparam name="T"></typeparam> 428 /// <param name="orderBy"></param> 429 /// <returns></returns> 430 public static string GetOrderBySql<T>(Expression<Func<IQueryable<T>, IOrderedQueryable<T>>> orderBy) where T : class 431 { 432 string result = string.Empty; 433 if (orderBy != null && orderBy.Body is MethodCallExpression) 434 { 435 MethodCallExpression exp = orderBy.Body as MethodCallExpression; 436 List<SqlParaModel> listSqlParaModel = new List<SqlParaModel>(); 437 result = MethodCallExpressionProvider(exp, listSqlParaModel); 438 } 439 if (result != string.Empty) 440 { 441 result = " order by " + result; 442 } 443 return result; 444 } 445 446 /// <summary> 447 /// lambda表达式转换sql 448 /// </summary> 449 /// <typeparam name="T"></typeparam> 450 /// <param name="fields"></param> 451 /// <returns></returns> 452 public static string GetQueryField<T>(Expression<Func<T, object>> fields) 453 { 454 StringBuilder sbSelectFields = new StringBuilder(); 455 if (fields.Body is NewExpression) 456 { 457 NewExpression ne = fields.Body as NewExpression; 458 for (var i = 0; i < ne.Members.Count; i++) 459 { 460 sbSelectFields.Append(ne.Members[i].Name + ","); 461 } 462 } 463 else if (fields.Body is ParameterExpression) 464 { 465 sbSelectFields.Append("*"); 466 } 467 else 468 { 469 sbSelectFields.Append("*"); 470 } 471 if (sbSelectFields.Length > 1) 472 { 473 sbSelectFields = sbSelectFields.Remove(sbSelectFields.Length - 1, 1); 474 } 475 return sbSelectFields.ToString(); 476 } 477 478 }
SqlParaModel如下:
1 public class SqlParaModel 2 { 3 /// <summary> 4 /// 5 /// </summary> 6 public string name { set; get; } 7 8 /// <summary> 9 /// 10 /// </summary> 11 public object value { set; get; } 12 }
demo:
1 class MyClass 2 { 3 public string id; 4 public string name; 5 public string desc; 6 public decimal price; 7 public int stock; 8 public bool isShow; 9 public DateTime createTime; 10 }
1 class Program 2 { 3 4 static void Main(string[] args) 5 { 6 //Expression<Func<MyClass, bool>> where = w => w.id == "123456"; 7 Expression<Func<MyClass, bool>> where = w => w.id.Contains("1"); 8 List<SqlParaModel> listSqlParaModel = new List<SqlParaModel>(); 9 var sql = LambdaToSqlHelper.GetWhereSql(where, listSqlParaModel); 10 } 11 12 }
参考:
http://www.cnblogs.com/kakura/p/6108950.html
http://www.cnblogs.com/zhyue/p/5690807.html
来源:https://www.cnblogs.com/maiaimei/p/7147049.html