lambda表達式轉換sql

来源:http://www.cnblogs.com/maiaimei/archive/2017/07/10/7147049.html
-Advertisement-
Play Games

這是我在博客園的第一遍文章,想分享下lambda表達式轉換sql。喜歡EF的便捷與優雅,不喜歡生成的一坨sql。(PS:公司封裝了一套訪問資料庫的方法,所以不確定是不是EF的問題,反正就是一坨密密麻麻的的sql,我有點點處女座小糾結,雖然我是天蝎座)好了,廢話少說。 using System;usi ...


這是我在博客園的第一遍文章,想分享下lambda表達式轉換sql。

喜歡EF的便捷與優雅,不喜歡生成的一坨sql。(PS:公司封裝了一套訪問資料庫的方法,所以不確定是不是EF的問題,反正就是一坨密密麻麻的的sql,我有點點處女座小糾結,雖然我是天蝎座)

好了,廢話少說。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace MaiCore
{
    /// <summary>
    ///
    /// </summary>
    public class LambdaToSqlHelper
    {
        /// <summary>
        /// NodeType枚舉
        /// </summary>
        private enum EnumNodeType
        {
            /// <summary>
            /// 二元運算符
            /// </summary>
            [Description("二元運算符")]
            BinaryOperator = 1,

            /// <summary>
            /// 一元運算符
            /// </summary>
            [Description("一元運算符")]
            UndryOperator = 2,

            /// <summary>
            /// 常量表達式
            /// </summary>
            [Description("常量表達式")]
            Constant = 3,

            /// <summary>
            /// 成員(變數)
            /// </summary>
            [Description("成員(變數)")]
            MemberAccess = 4,

            /// <summary>
            /// 函數
            /// </summary>
            [Description("函數")]
            Call = 5,

            /// <summary>
            /// 未知
            /// </summary>
            [Description("未知")]
            Unknown = -99,

            /// <summary>
            /// 不支持
            /// </summary>
            [Description("不支持")]
            NotSupported = -98
        }

        /// <summary>
        /// 判斷表達式類型
        /// </summary>
        /// <param name="exp">lambda表達式</param>
        /// <returns></returns>
        private static EnumNodeType CheckExpressionType(Expression exp)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.AndAlso:
                case ExpressionType.OrElse:
                case ExpressionType.Equal:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.GreaterThan:
                case ExpressionType.LessThan:
                case ExpressionType.NotEqual:
                    return EnumNodeType.BinaryOperator;
                case ExpressionType.Constant:
                    return EnumNodeType.Constant;
                case ExpressionType.MemberAccess:
                    return EnumNodeType.MemberAccess;
                case ExpressionType.Call:
                    return EnumNodeType.Call;
                case ExpressionType.Not:
                case ExpressionType.Convert:
                    return EnumNodeType.UndryOperator;
                default:
                    return EnumNodeType.Unknown;
            }
        }

        /// <summary>
        /// 表達式類型轉換
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static string ExpressionTypeCast(ExpressionType type)
        {
            switch (type)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    return " and ";
                case ExpressionType.Equal:
                    return " = ";
                case ExpressionType.GreaterThan:
                    return " > ";
                case ExpressionType.GreaterThanOrEqual:
                    return " >= ";
                case ExpressionType.LessThan:
                    return " < ";
                case ExpressionType.LessThanOrEqual:
                    return " <= ";
                case ExpressionType.NotEqual:
                    return " <> ";
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return " or ";
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return " + ";
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return " - ";
                case ExpressionType.Divide:
                    return " / ";
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return " * ";
                default:
                    return null;
            }
        }

        private static string BinarExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            BinaryExpression be = exp as BinaryExpression;
            Expression left = be.Left;
            Expression right = be.Right;
            ExpressionType type = be.NodeType;
            string sb = "(";
            //先處理左邊
            sb += ExpressionRouter(left, listSqlParaModel);
            sb += ExpressionTypeCast(type);
            //再處理右邊
            string sbTmp = ExpressionRouter(right, listSqlParaModel);
            if (sbTmp == "null")
            {
                if (sb.EndsWith(" = "))
                    sb = sb.Substring(0, sb.Length - 2) + " is null";
                else if (sb.EndsWith(" <> "))
                    sb = sb.Substring(0, sb.Length - 2) + " is not null";
            }
            else
                sb += sbTmp;
            return sb += ")";
        }

        private static string ConstantExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            ConstantExpression ce = exp as ConstantExpression;
            if (ce.Value == null)
            {
                return "null";
            }
            else if (ce.Value is ValueType)
            {
                GetSqlParaModel(listSqlParaModel, GetValueType(ce.Value));
                return "@para" + listSqlParaModel.Count;
            }
            else if (ce.Value is string || ce.Value is DateTime || ce.Value is char)
            {
                GetSqlParaModel(listSqlParaModel, GetValueType(ce.Value));
                return "@para" + listSqlParaModel.Count;
            }
            return "";
        }

        private static string LambdaExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            LambdaExpression le = exp as LambdaExpression;
            return ExpressionRouter(le.Body, listSqlParaModel);
        }

        private static string MemberExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            if (!exp.ToString().StartsWith("value"))
            {
                MemberExpression me = exp as MemberExpression;
                if (me.Member.Name == "Now")
                {
                    GetSqlParaModel(listSqlParaModel, DateTime.Now);
                    return "@para" + listSqlParaModel.Count;
                }
                return me.Member.Name;
            }
            else
            {
                var result = Expression.Lambda(exp).Compile().DynamicInvoke();
                if (result == null)
                {
                    return "null";
                }
                else if (result is ValueType)
                {
                    GetSqlParaModel(listSqlParaModel, GetValueType(result));
                    return "@para" + listSqlParaModel.Count;
                }
                else if (result is string || result is DateTime || result is char)
                {
                    GetSqlParaModel(listSqlParaModel, GetValueType(result));
                    return "@para" + listSqlParaModel.Count;
                }
                else if (result is int[])
                {
                    var rl = result as int[];
                    StringBuilder sbTmp = new StringBuilder();
                    foreach (var r in rl)
                    {
                        GetSqlParaModel(listSqlParaModel, r.ToString().ToInt32());
                        sbTmp.Append("@para" + listSqlParaModel.Count + ",");
                    }
                    return sbTmp.ToString().Substring(0, sbTmp.ToString().Length - 1);
                }
                else if (result is string[])
                {
                    var rl = result as string[];
                    StringBuilder sbTmp = new StringBuilder();
                    foreach (var r in rl)
                    {
                        GetSqlParaModel(listSqlParaModel, r.ToString());
                        sbTmp.Append("@para" + listSqlParaModel.Count + ",");
                    }
                    return sbTmp.ToString().Substring(0, sbTmp.ToString().Length - 1);
                }                
            }
            return "";
        }

        private static string MethodCallExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            MethodCallExpression mce = exp as MethodCallExpression;
            if (mce.Method.Name == "Contains")
            {
                if (mce.Object == null)
                {
                    return string.Format("{0} in ({1})", ExpressionRouter(mce.Arguments[1], listSqlParaModel), ExpressionRouter(mce.Arguments[0], listSqlParaModel));
                }
                else
                {
                    if (mce.Object.NodeType == ExpressionType.MemberAccess)
                    {
                        //w => w.name.Contains("1")
                        var _name = ExpressionRouter(mce.Object, listSqlParaModel);
                        var _value = ExpressionRouter(mce.Arguments[0], listSqlParaModel);
                        var index = _value.RetainNumber().ToInt32() - 1;
                        listSqlParaModel[index].value = "%{0}%".FormatWith(listSqlParaModel[index].value);
                        return string.Format("{0} like {1}", _name, _value);
                    }
                }
            }
            else if (mce.Method.Name == "OrderBy")
            {
                return string.Format("{0} asc", ExpressionRouter(mce.Arguments[1], listSqlParaModel));
            }
            else if (mce.Method.Name == "OrderByDescending")
            {
                return string.Format("{0} desc", ExpressionRouter(mce.Arguments[1], listSqlParaModel));
            }
            else if (mce.Method.Name == "ThenBy")
            {
                return string.Format("{0},{1} asc", MethodCallExpressionProvider(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel));
            }
            else if (mce.Method.Name == "ThenByDescending")
            {
                return string.Format("{0},{1} desc", MethodCallExpressionProvider(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel));
            }
            else if (mce.Method.Name == "Like")
            {
                return string.Format("({0} like {1})", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel).Replace("'", ""));
            }
            else if (mce.Method.Name == "NotLike")
            {
                return string.Format("({0} not like '%{1}%')", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel).Replace("'", ""));
            }
            else if (mce.Method.Name == "In")
            {
                return string.Format("{0} in ({1})", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel));
            }
            else if (mce.Method.Name == "NotIn")
            {
                return string.Format("{0} not in ({1})", ExpressionRouter(mce.Arguments[0], listSqlParaModel), ExpressionRouter(mce.Arguments[1], listSqlParaModel));
            }
            return "";
        }

        private static string NewArrayExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            NewArrayExpression ae = exp as NewArrayExpression;
            StringBuilder sbTmp = new StringBuilder();
            foreach (Expression ex in ae.Expressions)
            {
                sbTmp.Append(ExpressionRouter(ex, listSqlParaModel));
                sbTmp.Append(",");
            }
            return sbTmp.ToString(0, sbTmp.Length - 1);
        }

        private static string ParameterExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            ParameterExpression pe = exp as ParameterExpression;
            return pe.Type.Name;
        }

        private static string UnaryExpressionProvider(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            UnaryExpression ue = exp as UnaryExpression;
            var result = ExpressionRouter(ue.Operand, listSqlParaModel);
            ExpressionType type = exp.NodeType;
            if (type == ExpressionType.Not)
            {
                if (result.Contains(" in "))
                {
                    result = result.Replace(" in ", " not in ");
                }
                if (result.Contains(" like "))
                {
                    result = result.Replace(" like ", " not like ");
                }
            }
            return result;
        }

        /// <summary>
        /// 路由計算
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="listSqlParaModel"></param>
        /// <returns></returns>
        private static string ExpressionRouter(Expression exp, List<SqlParaModel> listSqlParaModel)
        {
            var nodeType = exp.NodeType;
            if (exp is BinaryExpression)    //表示具有二進位運算符的表達式
            {
                return BinarExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is ConstantExpression) //表示具有常數值的表達式
            {
                return ConstantExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is LambdaExpression)   //介紹 lambda 表達式。 它捕獲一個類似於 .NET 方法主體的代碼塊
            {
                return LambdaExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is MemberExpression)   //表示訪問欄位或屬性
            {
                return MemberExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is MethodCallExpression)   //表示對靜態方法或實例方法的調用
            {
                return MethodCallExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is NewArrayExpression) //表示創建一個新數組,並可能初始化該新數組的元素
            {
                return NewArrayExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is ParameterExpression)    //表示一個命名的參數表達式。
            {
                return ParameterExpressionProvider(exp, listSqlParaModel);
            }
            else if (exp is UnaryExpression)    //表示具有一元運算符的表達式
            {
                return UnaryExpressionProvider(exp, listSqlParaModel);
            }
            return null;
        }

        /// <summary>
        /// 值類型轉換
        /// </summary>
        /// <param name="_value"></param>
        /// <returns></returns>
        private static object GetValueType(object _value)
        {
            var _type = _value.GetType().Name;
            switch (_type)
            {
                case "Decimal ": return _value.ToDecimal();
                case "Int32": return _value.ToInt32();
                case "DateTime": return _value.ToDateTime();
                case "String": return _value.ToString();
                case "Char":return _value.ToChar();
                case "Boolean":return _value.ToBoolean();
                default: return _value;
            }
        }

        /// <summary>
        /// sql參數
        /// </summary>
        /// <param name="listSqlParaModel"></param>
        /// <param name="val"></param>
        private static void GetSqlParaModel(List<SqlParaModel> listSqlParaModel, object val)
        {
            SqlParaModel p = new SqlParaModel();
            p.name = "para" + (listSqlParaModel.Count + 1);
            p.value = val;
            listSqlParaModel.Add(p);
        }

        /// <summary>
        /// lambda表達式轉換sql
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="listSqlParaModel"></param>
        /// <returns></returns>
        public static string GetWhereSql<T>(Expression<Func<T, bool>> where, List<SqlParaModel> listSqlParaModel) where T : class
        {
            string result = string.Empty;
            if (where != null)
            {
                Expression exp = where.Body as Expression;
                result = ExpressionRouter(exp, listSqlParaModel);
            }
            if (result != string.Empty)
            {
                result = " where " + result;
            }
            return result;
        }

        /// <summary>
        /// lambda表達式轉換sql
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="orderBy"></param>
        /// <returns></returns>
        public static string GetOrderBySql<T>(Expression<Func<IQueryable<T>, IOrderedQueryable<T>>> orderBy) where T : class
        {
            string result = string.Empty;
            if (orderBy != null && orderBy.Body is MethodCallExpression)
            {
                MethodCallExpression exp = orderBy.Body as MethodCallExpression;
                List<SqlParaModel> listSqlParaModel = new List<SqlParaModel>();
                result = MethodCallExpressionProvider(exp, listSqlParaModel);
            }
            if (result != string.Empty)
            {
                result = " order by " + result;
            }
            return result;
        }

        /// <summary>
        /// lambda表達式轉換sql
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static string GetQueryField<T>(Expression<Func<T, object>> fields)
        {
            StringBuilder sbSelectFields = new StringBuilder();
            if (fields.Body is NewExpression)
            {
                NewExpression ne = fields.Body as NewExpression;
                for (var i = 0; i < ne.Members.Count; i++)
                {
                    sbSelectFields.Append(ne.Members[i].Name + ",");
                }
            }
            else if (fields.Body is ParameterExpression)
            {
                sbSelectFields.Append("*");
            }
            else
            {
                sbSelectFields.Append("*");
            }
            if (sbSelectFields.Length > 1)
            {
                sbSelectFields = sbSelectFields.Remove(sbSelectFields.Length - 1, 1);
            }
            return sbSelectFields.ToString();
        }

    }
}

 

-----------------------------------------------------------------------------------------------

demo:

    class Program
    {

        static void Main(string[] args)
        {
            //Expression<Func<MyClass, bool>> where = w => w.id == "123456";
            Expression<Func<MyClass, bool>> where = w => w.id.Contains("1");
            List<SqlParaModel> listSqlParaModel = new List<SqlParaModel>();
            var sql = LambdaToSqlHelper.GetWhereSql(where, listSqlParaModel);
        }

    }

    class MyClass
    {
        public string id;
        public string name;
        public string desc;
        public decimal price;
        public int stock;
        public bool isShow;
        public DateTime createTime;
    }

 

-----------------------------------------------------------------------------------------------

參考:

http://www.cnblogs.com/kakura/p/6108950.html
http://www.cnblogs.com/zhyue/p/5690807.html

 

歡迎挑出bug或者不成熟的地方。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • AWT概述 GUI全稱是Graphical User Interface,即圖形用戶界面,即應用程式提供給用戶操作的圖形界面,包括視窗、菜單、按鈕、工具欄和其它各種圖形界面元素 GUI設計提供了豐富的類庫,這些類分別位於java.awt和javax.swing包中,簡稱為AWT和Swing Swin ...
  • 不得不承認《Python游戲編程入門》這本書翻譯、排版非常之爛,但是裡面的demo還是很好的,之前做了些改編放到這裡。 先是素材: 背景 精靈 所有素材均取自此書 接下來就是精靈類的創建了: 將精靈類“放置”到游戲屏幕上,並加上背景 這樣的話精靈就在畫布上了,我們得讓它能左右移動: 然後實現跳躍及二 ...
  • 前面的話 選項卡Tabs是Web中一種非常常用的功能。用戶點擊對菜單項,能切換出對應的內容。本文將詳細介紹Bootstrap選項卡 基本用法 Bootstrap框架中的選項卡主要有兩部分內容組成: 1、選項卡菜單組件,對應的是 Bootstrap的 nav-tabs 2、可以切換的選項卡面板組件,在 ...
  • 關鍵詞:AngularJS指令與表達式、AngularJS中的MVC中的作用域、AngularJS過濾器、AngularJS中的 http && select && DOM操作、AngularJS中的表單驗證、AngularJS中的動畫、AngularJS中的路由 ...
  • 最近在做信開發時,發現<input type="file" />在IOS中可以拍照或從照片圖庫選擇,而Android系統則顯示資源管理器,無拍照選項,網上查找資料,改為<input type="file" capture="camera">後,Android可顯示相機和文檔,但IOS則只有拍照選項了 ...
  • 《NET 設計規範》第 2 章 框架設計基礎 要設計功能強大又易於使用的框架。 要理解廣大開發人員並有針對性地為他們設計框架。 要理解各種編程語言,併為他們設計框架。 要確保在設計任何包含公用API的特性時,把 API 設計規格書作為它最核心的部分。 要為每個主要的特性域定義一些最常見的使用場景。 ...
  • 這是很簡單。。 HTML <div> <input type="file" id="myfile"> <input type="button" value="上傳" onclick="HeadPortraitPicture()"> </div> JS代碼 function HeadPortraitP ...
  • C#編寫的代碼屬於跨平臺的托管代碼,C++語言可以編寫托管(managed)和非托管(native)代碼。在C#與C++的混合編程中,經常會使用C#來調用native C++的DLL,下麵有兩種常用的調用方法供大家參考。 使用P/Invoke直接調用native C++ Dll裡面的函數。(註:此方 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...