“造輪運動”之 ORM框架系列(一)~談談我在實際業務中的增刪改查_台中搬家公司

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

  想想畢業已經快一年了,也就是大約兩年以前,懷着滿腔的熱血正式跨入程序員的世界,那時候的自己想象着所熱愛的技術生涯會是多麼的豐富多彩,每天可以與大佬們坐在一起討論解決各種牛逼的技術問題,喝着咖啡,翹着二郎腿,大致就是下面這幅場景:

  

 

  可是現實卻總是那麼不盡如人意,現實的所謂技術生涯是永遠寫不完的增刪改查,還有那日漸稀薄的頭髮,哎,難…

  

 

    話說回來,如果連增刪改查都做不好,還談何技術生涯。

     所以,我一直認為:如果你認為自己是一個優秀的程序員,那就應該從最基礎的增刪改查中就能體現出來

     有人可能會說,增刪改查?很難嗎?

     其實,說難,也不難,無非就是寫SQL,Add、Delete、Update、Select,僅此而已;但是如果只是從SQL的角度認為這很容易,而對其不懈一顧,那你可能就和當初剛剛開始增刪改查大業的我一樣,太天真了。

    首先,我和大家分享一下我在工作這將近兩年中在增刪改查這條路上的心路歷程。

 

一、初出茅廬——原生sql走天下,sql寫到手抽筋

    某年某月某日,大三尾聲,翹課在宿舍睡大覺的我剛從床上艱難地爬起來,打開手機,發現剛工作的老學長阿威給我發來了一條信息:

   “阿森,來活了,幫我搞個網站,會弄嗎?”

   “網站?沒弄過誒。。”

     “這個項目給的銀兩還挺多的。。”

   “可以,可以,這個可以搞,沒問題,阿威哥”

     

  就這樣,我就走上了Web開發的道路。初入坑,首先遇到的問題就是數據庫的操作,但是經過自己的旁門左道的學習也大致摸清了使用ADO.NET操作數據庫的方法:

  第一步,使用DBConnection建立與數據庫之間的連接,打開連接

  第二步,然後創建DBCommand對象,初始化你想要執行的SQL語句

  第三步,調用DBCommand的方法,執行SQL語句

  第四步,使用DataReader逐條讀取sql執行的結果,或者使用DataAdapter類將結果填充(Fill)到DataSet中,最後關閉Connection連接

  到此為止,就實現了在.net中操作數據庫執行sql語句返回執行結果的操作。

  當然,我覺得任何一個初級的程序員也都會給自己封裝一個叫做SQLHelper的幫助類,我也不例外,它可以幫助你節省頻繁的建立數據庫連接(DBConnection)、初始化DBCommand對象、讀取sql執行結果等重複的代碼量。所以,封裝一個自己的SQLHelper工具類是一個初級程序員必備的素質。

  我把一個當初自己封裝的SQLHelper類貼出來獻獻醜:

   

  1     public static class SqlServerHelper
  2     {
  3         //數據庫連接字符串,從配置文件的配置字段中讀取
  4         private static readonly string connStr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;
  5 
  6         /// <summary>
  7         /// 創建新的數據庫連接
  8         /// </summary>
  9         public static SqlConnection CreateSqlConnection()
 10         {
 11             SqlConnection conn = new SqlConnection(connStr);
 12 
 13             conn.Open();   //打開數據庫連接
 14 
 15             return conn;
 16         }
 17 
 18         /// <summary>
 19         /// 執行無結果返回的sql語句(共用同一個連接)
 20         /// </summary>
 21         /// <param name="sqlConn"></param>
 22         /// <param name="sqlStr"></param>
 23         /// <param name="sqlParamters"></param>
 24         /// <returns>返回受影響的數據總條數</returns>
 25         public static long ExecuteNoQuery(SqlConnection sqlConn, string sqlStr, params SqlParameter[] sqlParamters)
 26         {
 27             using (SqlCommand cmd = sqlConn.CreateCommand())
 28             {
 29                 cmd.CommandText = sqlStr;
 30                 cmd.Parameters.AddRange(sqlParamters);
 31 
 32                 long sum = cmd.ExecuteNonQuery();
 33 
 34                 return sum;
 35             }
 36         }
 37 
 38         /// <summary>
 39         /// 執行無結果返回的sql語句(不共用一個連接)
 40         /// </summary>
 41         /// <param name="sqlStr"></param>
 42         /// <param name="sqlParamters"></param>
 43         /// <returns>返回受影響的數據總條數</returns>
 44         public static long ExecuteNoQuery(string sqlStr, params SqlParameter[] sqlParamters)
 45         {
 46             using (SqlConnection sqlConn = CreateSqlConnection())
 47             using (SqlCommand cmd = sqlConn.CreateCommand())
 48             {
 49                 cmd.CommandText = sqlStr;
 50                 cmd.Parameters.AddRange(sqlParamters);
 51 
 52                 long sum = cmd.ExecuteNonQuery();
 53 
 54                 return sum;
 55             }
 56         }
 57 
 58         /// <summary>
 59         /// 執行只有一行一列返回數據的sql語句(共用同一個連接)
 60         /// </summary>
 61         /// <param name="sqlConn"></param>
 62         /// <param name="sqlStr"></param>
 63         /// <param name="sqlParamters"></param>
 64         /// <returns>返回結果的第一行第一列數據</returns>
 65         public static object ExecuteScalar(SqlConnection sqlConn, string sqlStr, params SqlParameter[] sqlParamters)
 66         {
 67             using (SqlCommand cmd = sqlConn.CreateCommand())
 68             {
 69                 cmd.CommandText = sqlStr;
 70                 cmd.Parameters.AddRange(sqlParamters);
 71 
 72                 object res = cmd.ExecuteScalar();
 73 
 74                 return res;
 75             }
 76         }
 77 
 78         /// <summary>
 79         /// 執行只有一行一列返回數據的sql語句(不共用同一個連接)
 80         /// </summary>
 81         /// <param name="sqlStr"></param>
 82         /// <param name="sqlParamters"></param>
 83         /// <returns>返回結果的第一行第一列數據</returns>
 84         public static object ExecuteScalar(string sqlStr, params SqlParameter[] sqlParamters)
 85         {
 86             using (SqlConnection sqlConn = CreateSqlConnection())
 87             using (SqlCommand cmd = sqlConn.CreateCommand())
 88             {
 89                 cmd.CommandText = sqlStr;
 90                 cmd.Parameters.AddRange(sqlParamters);
 91 
 92                 object res = cmd.ExecuteScalar();
 93 
 94                 return res;
 95             }
 96         }
 97 
 98         /// <summary>
 99         /// 執行有查詢結果的sql語句(共用同一個連接)
100         /// </summary>
101         /// <param name="sqlConn"></param>
102         /// <param name="sqlStr"></param>
103         /// <param name="sqlParamters"></param>
104         /// <returns></returns>
105         public static DataTable ExcuteQuery(SqlConnection sqlConn, string sqlStr, params SqlParameter[] sqlParamters)
106         {
107             using (SqlCommand cmd = sqlConn.CreateCommand())
108             {
109                 cmd.CommandText = sqlStr;
110 
111                 cmd.Parameters.AddRange(sqlParamters);
112 
113                 //把sql語句的執行結果填充到SqlDataAdapter中
114                 SqlDataAdapter adapter = new SqlDataAdapter(cmd);
115 
116                 DataTable dt = new DataTable();
117                 adapter.Fill(dt);    //將執行結果填充到dt對象中
118 
119                 return dt;
120             }
121         }
122 
123         /// <summary>
124         /// 執行有查詢結果的sql語句(共用同一個連接)
125         /// </summary>
126         /// <param name="sqlStr"></param>
127         /// <param name="sqlParamters"></param>
128         /// <returns></returns>
129         public static DataTable ExcuteQuery(string sqlStr, params SqlParameter[] sqlParamters)
130         {
131             using (SqlConnection sqlConn = CreateSqlConnection())
132             using (SqlCommand cmd = sqlConn.CreateCommand())
133             {
134                 cmd.CommandText = sqlStr;
135 
136                 cmd.Parameters.AddRange(sqlParamters);
137 
138                 //把sql語句的執行結果填充到SqlDataAdapter中
139                 SqlDataAdapter adapter = new SqlDataAdapter(cmd);
140 
141                 DataTable dt = new DataTable();
142                 adapter.Fill(dt);    //將執行結果填充到dt對象中
143 
144                 return dt;
145             }
146         }
147 
148         /// <summary>
149         /// 批量插入數據到數據庫
150         /// </summary>
151         /// <param name="insertTable"></param>
152         /// <param name="dataTableName"></param>
153         public static void BatchInsert(DataTable insertTable, string dataTableName)
154         {
155             using (SqlBulkCopy sbc = new SqlBulkCopy(connStr))
156             {
157                 sbc.DestinationTableName = dataTableName;
158 
159                 for (int i = 0; i < insertTable.Columns.Count; i++ )
160                 {
161                     sbc.ColumnMappings.Add(insertTable.Columns[i].ColumnName, insertTable.Columns[i].ColumnName);
162                 }
163 
164                 sbc.WriteToServer(insertTable);
165             }
166         }
167 
168     }

SQLHelper

    於是在那段開發的歲月里,是這個SQLHelper類陪我走完了全程,向它說聲辛苦了… 

 

二、EF框架真香,Lambda表達式我最愛

  大三暑假,由於抑制不住想要出去試一試的衝動,我來到了一家小型互聯網公司實習,公司也是用.net開發網站的技術棧。

   當上崗的第一天,我準備掏出我自認為牛逼的SQLHelper的時候,卻被項目經理叫停了,項目經理阿勇對我說:

   “小伙子,21世紀了,還在傻傻地用SQLHelper?來,試試EF吧,讓你欲罷不能…”

   於是,一臉懵逼的我,一頭埋進了EF的世界中,不得不說,的確香。

   EF全名EntityFramework,是微軟官方的一款ORM框架,支持Object(對象)與數據庫之間的數據映射,支持Linq的操作語法,受廣大.NET程序員青睞。

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

   其實在接觸EntityFramework之前我就使用過Dapper,Dapper相對與EF來說是輕量的多的一款ORM框架,近乎於原生sql的寫法,讓它處於性能之最,也提供對象與數據庫表之間的映射。

   但正是由於用過了EF,我才知道了原來還有 Lambda to SQL 這種東西,相信大多數程序員都和我一樣經歷過寫原生SQL的痛苦

   無論是多麼簡單的一條sql,你都要在表設計文檔和sql編輯器中反覆切換比對字段,生怕自己打錯一個字母;而且我們在基礎的業務中的大部分增刪改查其實都是相當簡單的單表操作sql,正是使用了Lambda to SQL 的代碼編寫方式,你可以在VsStudio豐富的代碼提示下飛快地完成一條簡單的SQL語句。

   為什麼說簡單的SQL編寫才使用Lambda表達式的形式去編寫呢,複雜的sql就不行了嗎?

   理由是,你要明白不管是Lambda還是Linq的語句,最終都是在EF的SQL語句解析器中被翻譯成可供數據庫執行的sql語句,不得不說EF的Linq To SQL 的解析器的確強大,但是畢竟還是機器翻譯的sql,當你讓它給你轉化一個複雜的linq語句時,它是一定考慮不到sql性能的優化。

   就像你去用金山詞霸翻譯一個單詞,它可以給你翻譯的很準確;但是你讓它去幫你翻譯一個長句,可能你讀着就覺得彆扭了;更有甚者把一篇論文粘貼複製到金山詞霸里,那可能比你直接看英文原文還費勁,這是同樣一個道理,有些東西還是需要人工來做,就比如優化sql語句。不能奢求什麼都扔給Lambda和Linq,它幫我們做的已經足夠多了。

   總之一句話,Lambda很香,但絕對不能替代SQL

 

三、全部都是存儲過程,調試太難

  畢業后的我來到了一家大型製造業企業從事IT行業,說的這麼體面,其實還是一個苦逼敲代碼的。

   上崗的第一天我就問旁邊的程序媛,對,你沒看錯,是“媛”,不是“猿”,嘿嘿。想我代碼生涯一年以來,第一次見這麼多程序媛,低調低調,不要聲張,不然讓隔壁的程序猿聽到了會嫉妒的。

   

  “姐姐,我們這用EF嗎?”

   “EF?是啥…”,姐姐一臉懵逼。。。

   “那你們用什麼操作數據庫啊?自己封裝SQLHelper嗎?”

   “用存儲過程啊,都用了7、8年了”

   “哦,這樣啊”,我心想,都用了7、8年了,這個姐姐是個狠人。

   入鄉隨俗,咱也不能壞了規矩,那就照着用吧,存儲過程以前用過幾次,但是那是在sql的邏輯比較複雜的時候才會寫存儲過程調用的,要是所有的sql都放在存儲過程裏面去調用的話,那就太繁瑣了吧。

   經過一段時間的使用,證明我的顧慮是對的。

   全部使用存儲過程后,發現,就是一條簡單的Delete、Insert語句都要寫在存儲過程中,簡直不要太麻煩。

   首先的問題就是,不好編寫,對於一個剛上手存儲過程語法的程序員來說這簡直就是災難,目前的SQL編輯器沒有語法提示功能而造成非常不好的代碼編寫體驗,嚴重拖慢開發進程;

   更要命的是,太難調試!當你在c#代碼中調用完存儲過程后,你發現程序安然無恙地執行完,但是並沒有得到你想要的結果,於是你找啊找,找啊找,最後,你只能懷疑是不是存儲過程有問題,但是運行過程中存儲過程一點動靜也沒有啊,這就是使用存儲過程最大的弊端,悄無聲息地出錯,完全不知道裏面發生了什麼,只能默默地把存儲過程的參數記下,然後輸入到sql的調試其中執行一遍才可以發現錯誤。

   然後你的項目經理跑過來問你昨天的代碼敲完了嗎,心累…

   於是,我對存儲過程留下了非常不好的印象。但是存儲過程的好處是顯而易見的:當你的業務邏輯用一條sql解決不了的時候你會考慮使用多條sql來配合完成查詢,可是如果執行每一條sql語句都要與數據庫建立連接然後再執行的話好像有點繁瑣不說,主要是重建數據庫連接的過程也是需要時間與資源的,所以這個時候存儲過程就派上用場了,把你的sql都扔到存儲過程裏面去吧,多麼令人心曠神怡的操作,這個時候你就不要去想存儲過程出錯難找的問題了,因為你想要獲得方便總要有點犧牲精神。

 

四、沒有哪一種是完全適合自己的,如果有,那就是自己造的

  想要有Dapper的神速,又想要有EF中使用lambda表達式的便捷,但是二者好像並不能兼得。

   Dapper雖然是效率之王,但是它沒有EF中可以使用的便捷的lambda表達式;

   EF雖然可以使用便捷的lambda、linq語法,可是由於EF的過分強大(EF還具有對象追蹤的功能),讓它顯得稍微有點臃腫,屬於重量級的ORM框架。

   於是我就在想,有沒有一款輕量級的ORM,既有不低的執行效率,又有簡單實用的Lambda TO SQL?

   答案當然是,有的,現在開源的國產sqlSuger、freeSql,比比皆是。但是,我就是想自己試試造輪子的感覺,目的並不是說造出多麼好的ORM框架去超越他們,而是在這個過程中學到的東西和得到的歷練是非常可觀的。

 

   沒辦法,有觀眾說“褲子都脫了,就給我看這個?”,我決定還是先把藏的貨先擺出來,也好有個交代…

   源碼地址:https://gitee.com/xiaosen123/CoffeeSqlORM

   本文為作者原創,轉載請註明出處:https://www.cnblogs.com/MaMaNongNong/p/12884871.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家