一. ASP.NET體系css
從事.Net開發以來,最早接觸的Web開發框架是Asp.Net WebForm,該框架高度封裝,爲了隱藏Http的無狀態模式,ViewState功不可沒,經過的控件的拖拽和綁定,很快就能夠搭建出來一個Web項目,其開發速度遠勝Java、PHP,當年Web項目並不很重視體驗,沒有今天響應式,沒有各類前端js框架,因此在當年的WebForm,微軟是以引覺得豪的。html
該框架畢竟有時代侷限性,隨着前端的崛起,隨着人們對項目體驗的提升,隨着項目的體量的增大,WebForm在當下強調鬆耦合、模塊化、組件化的時代,略顯落後,故微軟於2009年推出第一個開源框架,即MVC框架,俗稱ASP.Net MVC1,後面又陸續的推出 MVC二、三、四、5。如今最新的跨平臺Web開發框架 Core MVC,它隸屬於.Net Core框架,而非 .Net FrameWork下。前端
下面用一張圖表示一下在Core出現以前,.Net在Web開發即ASP.NET中包含的內容。node
簡單分析一下:WebPages(沒用過哎)、WebForms、MVC均爲ASP.NET下的Web開發框架,WebAPI負責構建HTTP常規服務,SignalR負責構建實時服務。web
MVC和WebAPI簡單對比一下:MVC用來構建網站既關心頁面又關心數據,WebAPI只關心數據;MVC經過能夠經過Action的名字來接受CURD操做,而WebAPI經過HTTP的訪問方式(GET、PUT、POST、DELETE)來表達不一樣的CURD操做。ajax
(WebService、WCF、WebAPI、SignalR在後面會有單獨的章節詳細介紹)數據庫
二. WebForm和MVC對比編程
1. WebFormwindows
作過WebForm開發的朋友們應該很清楚,WebForm開發形式主要分爲三種:設計模式
①:服務器端控件,即拖拽控件(aspx)
②:通常處理程序(ashx) + html模板
③:html靜態頁 + ajax + 通常處理程序(ashx)
請求模型:
WebForm的幾個臭名昭著的缺點:封裝太強,致使開發不靈活;ViewState的無狀態;聲明週期模型複雜,對於初學者不易於理解。
2. MVC
咱們常常從各類開發工程師(JAVA 、.NET、PHP)口中聽到MVC,但不一樣人說的MVC貌似相似,但又有區別,那麼MVC究竟是什麼呢?(相似的還有MVP和MVVM)
1. 從宏觀上來講:MVC是框架分層的一種搭建思想,在最原始的項目中,沒有什麼框架分層之說,全部的項目代碼都在一個層裏,這樣會致使代碼冗雜,耦合性強,項目迭代升級困難,MVC是一種分層思想,將一個項目代碼分爲幾類,分別放到不一樣的層裏,Model層存儲一些數據和業務邏輯,View層處理頁面問題,Controller層用來接收人機交互指令。MVC分層思想和傳統的三層(數據庫訪問層、業務邏輯層、表現層)仍是有區別的。
MVC的核心是分離了View和Model(即頁面和數據),Controller負責接收和發送指令,交給Model層處理。
2. 從.Net的角度來講,MVC是微軟提供一個Web開發框架,全稱ASP.NET MVC,它是傳統三層中View層的一種功能細分,一種表現形式而已,與三層沒有直接關係。ASP.NET MVC框架中的開發流程圍繞:
① Model:存儲實體類,實現系統中的業務邏輯。
② View:頁面展現(Razor模板),展示給用戶。
③ Controller:頁面的邏輯處理,用來與用戶的交互,處理接受的Http請求。而後從Model中獲取相應的數據,轉發給View進行頁面展現。
補充:經過Controller中的Action,能夠充當APP的服務端接口的做用,和WebAPI達到的效果一致,但訪問方式是有區別的。
請求模型:
下面列舉MVC框架的幾個典型的優勢:
①:鬆耦合,M-V-C分工明確,有利於代碼的維護。
②:便於Seo優化,能自定義url的生成規則。
③:ASP.NET MVC5是個出色的表現層框架。
④:對單元測試支持友好。
三. ASP.Net MVC 框架
1. 包含的技術
ASP.NET MVC框架,這裏以MVC5爲例,涉及到的知識有:Model-View-Controller的使用、Area和Global的理解、路由配置、數據傳遞的方式、AOP思想的體現(4大過濾器),各類Result、Razor語法、HttpContext相關的五大對象、分佈視圖、特性的補充、Html擴展控件、Bundles壓縮js和css的原理、Owin約束、管道模型及MVC的工做原理。
以及最新的Core MVC框架的相關知識。
2. 常見的約定
①:控制器命名一般以Controller結尾。
②:一個控制器對應一個View視圖文件夾,經常使用頁面都存放在裏面。
四. 系列章節
第二節:各類路由約束(動態路由、靜態路由、組合路由、正則約束、命名空間約束、區域內路由)
第三節:Action向View傳值的四種方式(ViewData、ViewBag、TempData、Model)
第四節:MVC中AOP思想的體現(四種過濾器)並結合項目案例說明過濾器的實際用法
第五節:從源碼的角度理解MVC中各類Result(ActionResult、JsonResult、JavaScriptResult等)---待補充
第八節:layout和partialView、RenderPartial和Partial、 action和RenderAction(未完成)
第十節:數據批註(DataAnnotationModel)和自定義驗證(包括Model級別的驗證)
第十三節:HttpHander擴展及應用(自定義擴展名、圖片防盜鏈)
第十五節:HttpContext五大核心對象的使用(Request、Response、Application、Server、Session)
第十六節:利用MVC的擴展完成「一個後臺,多套前端頁面」的解決方案(未完成)
ORM系列之Entity FrameWork詳解(持續更新)
一. 談情懷
從第一次接觸開發到如今(2018年),大約有六年時間了,最初階段鏈接數據庫,使用的是【SQL語句+ADO.NET】,那時候,什麼存儲過程、什麼事務 通通不理解,生硬的將SQL語句傳入SQLHelper類中,後來知道還要注意SQL注入,引入參數化的方式,如今回想一下,那個階段簡直Low到爆。
(PS:如今不少場景下仍是須要使用ADO.NET,因此沒有最好,只有最適合)
凡事都是從零開始的,正是這個階段積累,纔有了後續的發展。一次機會,接觸到了微軟的EF(ps:若是沒有記錯的話,當時用的是EF 5.0 的DBFirst模式),完全顛覆了個人開發觀念,不須要寫SQL語句(那時還不清楚EF也能夠調用SQL語句),Lambda表達式(並不知道Lambda還分Lambda表達式和Lambda語句),興奮了一陣,而後進入了漫長的EF摸索和填坑階段了(EF5.0→EF6.2→EF Core)。
若是你以爲EF不靈活,不能調用存儲過程或自定義事務?若是你以爲EF在處理海量數據庫的時候會很慢?若是你以爲EF知足不了你的開發需求?。。。。。。 那麼也許該系列的文章會對你有些幫助。
二. ORM框架
提及DotNet領域的ORM框架,咱們想到的有EF、NHibernate、FluentData、Dapper、SQLSugar等等。那麼到底什麼是ORM呢?
ORM(Object Relational Mapping),中文名字:對象關係映射,它是面向對象思想的進一步的發揚光大,主要實現【程序對象】到【關係型數據庫】的映射。在以前使用ADO.NET開發的時候,數據庫訪問層(即 DAL),要寫不少增長、刪除、保存的方法,非常麻煩,若是引入ORM框架,它自帶CRUD的API(至關於DAL層已經給你寫好了),能夠自動生成SQL語句,因此做爲開發者而言,只須要關注對象便可。
ORM的含義:
① O:Domain Object 領域模型
② R:Relational DataBase 關係型數據庫
③ M : Mapping 映射關係
ORM的三大核心原則:
① 簡單性:以最簡單的模式建模數據。
② 傳達性:數據庫結構被任何人都能理解的語言文檔化。
③ 精確性:基於數據模型建立正確標準化的結構。
EF的核心架構:
① EDM(Entity Data Model):這是微軟提供的一個強大的可視化工具,用來生成 O、R、M的
② 兩種語法的查詢:LINQ to Entities 和 Entity SQL
③ ObjectServices:Linq to Entities 和Entity Client Data Provider 之間起到轉換做用
④ Entity Client Data Provider: 將Lambda轉換成SQL語句
⑤ ADO.Net Data Provider: 標準的ADO.Net
⑥ DataBase:數據庫
三. 從EF的入手,深刻理解ORM框架
EF(Entity FrameWork),是微軟推出基於ADO.NET的數據庫訪問技術,它是一套ORM框架, 框架中包括了實例模型、數據模型、關係映射三部分,EF的上下文爲DbContext,EF在 【.Net FrameWork】版本中最新的是 6.2.0 版本,最穩定的是6.2版本,在【.Net Core】中推出 EF Core (即所謂的「EF7」,^_^,固然已經不這麼叫了)。
EF包括三種模式:DBFirst、CodeFist、ModelFirst 。EF能夠調用SQL語句、可使用Linq查詢、可使用Lambda查詢,EF還有不少高級屬性,好比延遲加載、緩存屬性等等,在該系列文章中都將逐一詳細介紹。
EF也有優缺點:
優勢:① EF是微軟自家的產品,和VS的集成度很高,極大的提升了開發效率。
② EF中ModelFirst模式提供的模型設計器功能很強大,且採用T4模板生成的代碼的功能很強大。
③ 支持多數據庫開發,簡單的改變配置文件,就能夠切換數據庫。
缺點: 在複雜的查詢生成SQL腳本的效率不是很高,浪費了性能。
舉例:以前在開發中,一張幼兒表裏大約有60個字段左右,一個業務須要根據複雜的條件查詢出來一些信息(大約5個字段左右),當時因爲按開發人員的失誤,寫的Lambda表達式直接將全部字段查詢出來,致使該業務耗時很長大約25s左右,後來在個人引導下,先去數據庫中直接寫SQL語句,查詢全部字段,發現速度很快;而後將EF中的Lambda查詢改成只查詢指定的5個字段,發現速度由25s→3s左右,提高很是明顯,固然這也很好的說明了EF的侷限是在生成SQL的腳本效率上。
四. 系列章節
第一節: 結合EF的本地緩存屬性來介紹【EF增刪改操做】的幾種形式。
第二節: 比較EF的Lambda查詢和Linq查詢寫法的區別。
第三節: EF調用普通SQL語句的兩類封裝(ExecuteSqlCommand和SqlQuery )
第四節: EF調用存儲過程的通用寫法和DBFirst模式子類調用的特有寫法
第五節: EF高級屬性(一) 之 本地緩存、當即加載、延遲加載(不含導航屬性)
第六節: EF高級屬性(二) 之延遲加載、當即加載、顯式加載(含導航屬性)。
第七節: EF的三種事務的應用場景和各自注意的問題(SaveChanges、DBContextTransaction、TransactionScope)。
第八節: EF的性能篇(一) 之 EF自有方法的性能測試 :
第九節: EF的性能篇(二) 之 Z.EntityFramework.Extensions程序集解決EF的性能問題 :
第十節: EF的三種追蹤實體屬性變化方式(DBEntityEntry、ChangeTracker、Local) :
第十一節: EF的三種模式(一) 之 DBFirst模式(SQLServer和MySQL兩套方案):
第十二節: EF的三種模式(二) 之 ModelFirst模式(SQLServer爲例):
第十三節: EF的三種模式(三) 之 來自數據庫的CodeFirst模式 :
第十四節: EF的三種模式(四) 之 原生正宗的 CodeFirst模式的默認約定 :
第十五節: EF的CodeFirst模式經過DataAnnotations修改默認協定 :
第十六節: EF的CodeFirst模式經過Fluent API修改默認協定 :
第十七節: EF的CodeFirst模式的四種初始化策略和經過Migration進行數據的遷移 :
第十八節: EF的CodeFirst模式鏈接MySQL數據庫的解決方案(未完成) :
第十九節: 結合【表達式目錄樹】來封裝EF的BaseDal層的方法 :
第二十節: 深刻理解併發機制以及解決方案(鎖機制、EF自有機制、隊列模式等):
第二十一節:ADO層次上的海量數據處理方案(SqlBulkCopy類插入和更新):
第二十二節: 以SQLServer爲例介紹數據庫自有的鎖機制(共享鎖、更新鎖、排它鎖等)(未完成) :
第二十三節: EF性能篇(三)之開源組件 Z.EntityFrameWork.Plus.EF6解決EF性能問題 :
第十六節:語法總結(3)(C#6.0和C#7.0新語法)
一. C# 6.0 新語法
1. 自動屬性初始化能夠賦值
1 /// <summary> 2 /// 自動屬性初始化 3 /// </summary> 4 public class UserInfor 5 { 6 public string userId { get; set; } = "123456"; 7 8 public string userName { get; set; } = "lmr"; 9 10 } 11 { 12 Console.WriteLine("------------------------- 1. 自動屬性初始化能夠賦值----------------------------"); 13 UserInfor uInfor = new UserInfor(); 14 Console.WriteLine($"id={uInfor.userId},userName={uInfor.userName}"); 15 }
2. 字符串嵌入值【$配合{}使用】
特別注意:{}中若是有業務計算須要配合()使用
1 { 2 Console.WriteLine("------------------------- 2. 字符串嵌入值【$配合{}使用】----------------------------"); 3 UserInfor uInfor2 = new UserInfor(); 4 Console.WriteLine($"名字爲:{uInfor2.userName}"); 5 //表明要輸入一個括號{} 6 Console.WriteLine($"名字爲:{{{uInfor2.userName}}}"); 7 //{}中若是有業務計算須要配合()使用 8 Console.WriteLine($"{(uInfor2.userName != "lmr" ? "小鮮肉" : "老鮮肉")}"); 9 }
3. 能夠在命名空間出使用static聲明靜態類
1 { 2 Console.WriteLine($"-------------------------3.能夠在命名空間出使用static聲明靜態類-------------------------"); 3 Console.WriteLine($"以前的使用方式: {Math.Pow(4, 2)}"); 4 Console.WriteLine($"導入後可直接使用方法: {Pow(4, 2)}"); 5 }
4. 空值運算符(?)
不須要判斷是否爲空了.
1 { 2 Console.WriteLine($"-------------------------4. 空值運算符-------------------------"); 3 int? iValue = 10; 4 Console.WriteLine(iValue?.ToString());//不須要判斷是否爲空 5 string name = null; 6 Console.WriteLine(name?.ToString()); 7 }
5. 對象初始化
能夠直接給字典類型經過相似「索引」的形式賦值 (原先是經過Add方法賦值)
1 { 2 Console.WriteLine($"-------------------------5. 字典對象初始化-------------------------"); 3 Dictionary<string, string> dicList = new Dictionary<string, string>() 4 { 5 ["1"] = "ypf1", 6 ["2"] = "ypf2", 7 ["3"] = "ypf3" 8 }; 9 Dictionary<string, string> dicList2 = new Dictionary<string, string>() 10 { 11 { "1","lmr1"}, 12 { "2","lmr2"}, 13 { "3","lmr3"} 14 }; 15 foreach (var item in dicList) 16 { 17 Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString()); 18 } 19 foreach (var item in dicList2) 20 { 21 Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString()); 22 } 23 }
6. 異常過濾器增長when判斷條件
只有符合when裏的條件,才能進入catch,若是不知足的話,直接代碼報錯,不能拋異常
1 { 2 Console.WriteLine("-------------------------6. 異常過濾器增長when判斷條件-------------------------"); 3 int epCheck = 100; 4 try 5 { 6 int.Parse("ypf"); 7 } 8 catch (Exception e) when (epCheck > 11) 9 { 10 Console.WriteLine(e.Message); 11 } 12 }
7. nameof表達式
把實例轉換成同名的字符串
1 { 2 Console.WriteLine("-------------------------7. nameof表達式-------------------------"); 3 UserInfor userInfor = new UserInfor(); 4 Console.WriteLine(nameof(userInfor)); 5 }
8. 在catch和finally語句塊裏使用await(暫不介紹,不經常使用)
9. 在屬性或方法上使用Lambada表達式
①:方法針對於只有一行的方法
②:屬性經過Lambda的形式進行賦值
public class UserInfor { public string userId { get; set; } = "123456"; public string userName { get; set; } = "lmr"; /// <summary> /// Lambda類型的方法 /// </summary> public void LambdaPrint() => Console.WriteLine("我是Lambda類型的方法"); public string userSex => string.Format("男"); } { Console.WriteLine("-------------------------9.在屬性或方法上使用Lambada表達式-------------------------"); UserInfor userInfor = new UserInfor(); userInfor.LambdaPrint(); Console.WriteLine($"userSex的值爲:{userInfor.userSex}"); }
二. C# 7.0 新語法
1. out參數的改進
在C#7.0以前, out參數使用前必須先聲明,而後傳入方法中,在C#7.0後能夠直接在傳入方法的時候進行聲明。
1 /// <summary> 2 /// out參數的改進 3 /// </summary> 4 /// <param name="x"></param> 5 /// <param name="y"></param> 6 public void DoNoting(out int x, out int y) 7 { 8 x = 1; 9 y = 2; 10 } 11 { 12 Console.WriteLine("--------------------1-out參數的改進------------------------"); 13 SevenEdition s = new SevenEdition(); 14 s.DoNoting(out int x, out int y); 15 Console.WriteLine(x + y); 16 }
2. 元組(Tuples)
①:須要經過nuget安裝這個System.ValueTuple包
②:元組建立的三種方式:語法糖、Create靜態方法、構造函數 (默認狀況下是經過Item一、Item2 。。。)
③:指定元祖命名:能夠經過左邊指定,也能夠經過右邊指定
④:將元祖應用於方法中返回值,即一個方法能夠返回多種不一樣類型的值,不須要封裝實體便可以實現。
1 /// <summary> 2 /// 默認元組命名 3 /// </summary> 4 /// <returns></returns> 5 public (int, string, string) TupleWay1() 6 { 7 return (1, "ypf1", "ypf2"); 8 } 9 /// <summary> 10 /// 指定元祖命名 11 /// </summary> 12 /// <returns></returns> 13 public (int m, string n, string k) TupleWay2() 14 { 15 return (1, "ypf1", "ypf2"); 16 } 17 { 18 Console.WriteLine("--------------------2-元組(Tuples)------------------------"); 19 //1. 建立元組的三種形式 20 Console.WriteLine("--------------------1. 建立元組的三種形式------------------------"); 21 var tuple1 = (1, 2); //語法糖 22 var tuple2 = ValueTuple.Create("ypf", "lmr"); //Create靜態方法 23 var tuple3 = new ValueTuple<int, string>(12, "ypf12"); //構造函數 24 Console.WriteLine($"tuple1的兩個值爲:{tuple1.Item1}和{tuple1.Item2}"); 25 Console.WriteLine($"tuple1的兩個值爲:{tuple2.Item1}和{tuple2.Item2}"); 26 Console.WriteLine($"tuple1的兩個值爲:{tuple3.Item1}和{tuple3.Item2}"); 27 28 //2. 指定元祖命名 29 Console.WriteLine("--------------------2. 指定元祖命名------------------------"); 30 (int m, string n) tuple4 = (100, "erp"); //左邊命名 31 Console.WriteLine($"tuple4的兩個值爲:{tuple4.m}和{tuple4.n}"); 32 var tuple5 = (one: 250, two: "spz"); //右邊命名 33 Console.WriteLine($"tuple5的兩個值爲:{tuple5.one}和{tuple5.two}"); 34 35 //3. 元祖應用於方法中返回值 36 Console.WriteLine("--------------------3. 元祖應用於方法中返回值------------------------"); 37 SevenEdition s = new SevenEdition(); 38 //默認命名 39 var result1 = s.TupleWay1(); 40 Console.WriteLine($"返回值有:{result1.Item1}、{result1.Item2}、{result1.Item3}"); 41 //指定命名 42 var result2 = s.TupleWay2(); 43 Console.WriteLine($"返回值有:{result2.m}、{result2.n}、{result2.k}"); 44 45 }
3. 局部函數
即在{}中聲明一個函數,只有{}中能使用
1 { 2 Console.WriteLine("--------------------3-局部函數------------------------"); 3 DoSome(); 4 void DoSome() 5 { 6 Console.WriteLine("我是局部函數"); 7 } 8 }
4. 模式的比較和匹配
①:is的新模式。
首先補充一個概念:拆箱和裝箱
拆箱是將引用類型→值類型 (object ,class均爲引用類型)
裝箱是將值類型→引用類型
案例:有一個object類型的變量a,若是它是int類型,則+10賦值給m,並輸出m的值,下面看新老用法比較。
1 { 2 Console.WriteLine("--------------------4-模式比較(is)------------------------"); 3 object a = 9; 4 //老寫法 5 if (a is int) 6 { 7 int b = (int)a; //拆箱 8 int c = b + 10; 9 Console.WriteLine($"老寫法c的值爲:{c}"); 10 } 11 //新寫法 12 if (a is int m) //這裏的a直接拆箱爲m 13 { 14 int c = m + 10; 15 Console.WriteLine($"新寫法c的值爲:{c}"); 16 } 17 }
②:switch-case中能夠自定義參數類型
傳統狀況,全部的case中必須是同類型的;而在C# 7.0 新版本中,case中能夠不一樣類型,即Swich中能夠傳入任何類型,而後經過case中進行對應匹配,這就叫作模式匹配。

1 /// <summary> 2 /// 單一類型 3 /// </summary> 4 /// <param name="m"></param> 5 /// <returns></returns> 6 public dynamic SwitchWay1(string m) 7 { 8 dynamic data; 9 switch (m) 10 { 11 case "ypf1": 12 data = m + "lmr1"; 13 break; 14 case "ypf2": 15 data = m + "lmr2"; 16 break; 17 default: 18 data = "spz00000"; 19 break; 20 } 21 return data; 22 } 23 24 /// <summary> 25 /// 多種類型 26 /// </summary> 27 /// <param name="m"></param> 28 /// <returns></returns> 29 public dynamic SwitchWay2(object m) 30 { 31 dynamic data; 32 switch (m) 33 { 34 case int a when a > 10: 35 data = a + 10; 36 break; 37 case int b: 38 data = b + 100; 39 break; 40 case String c: 41 data = c + "mmmmmmmmmmmmmmm"; 42 break; 43 default: 44 data = "spz00000"; 45 break; 46 } 47 return data; 48 }
1 { 2 Console.WriteLine("--------------------4-模式匹配(switch-case)------------------------"); 3 SevenEdition sE = new SevenEdition(); 4 //老用法 5 var data1 = sE.SwitchWay1("ypf1"); 6 Console.WriteLine($"類型爲:{data1.GetType()},值爲:{data1}"); 7 //新用法 8 var data2 = sE.SwitchWay2(1); 9 Console.WriteLine($"類型爲:{data2.GetType()},值爲:{data2}"); 10 11 var data3 = sE.SwitchWay2(11); 12 Console.WriteLine($"類型爲:{data3.GetType()},值爲:{data3}"); 13 14 var data4 = sE.SwitchWay2("ypf1"); 15 Console.WriteLine($"類型爲:{data4.GetType()},值爲:{data4}"); 16 17 }
5. 數字文本語法的改寫
好比10000000 能夠寫成10_000_000 ,方便識別。
{ Console.WriteLine("--------------------5-數字文本語法的改寫------------------------"); long a = 10000000; long b = 10_000_000; Console.WriteLine($"a的值爲:{a},b的值爲:{b}"); }
6. 補充一些特殊地方也能夠寫異常表達式 (不詳細測試了)
好比:條件表達式(? :)、null合併運算符(??)、一些Lambda
eg:private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName));
第三節:深度剖析各種數據結構(Array、List、Queue、Stack)及線程安全問題和yeild關鍵字
一. 各種數據結構比較及其線程安全問題
1. Array(數組):
分配在連續內存中,不能隨意擴展,數組中數值類型必須是一致的。數組的聲明有兩種形式:直接定義長度,而後賦值;直接賦值。
缺點:插入數據慢。
優勢:性能高,數據再多性能也沒有影響
特別注意:Array不是線程安全,在多線程中須要配合鎖機制來進行,若是不想使用鎖,能夠用ConcurrentStack這個線程安全的數組來替代Array。
1 { 2 Console.WriteLine("---------------------------01 Array(數組)-----------------------------------"); 3 //模式一:聲明數組並指定長度 4 int[] array = new int[3]; 5 //數組的賦值經過下標來賦值 6 for (int i = 0; i < array.Length; i++) 7 { 8 array[i] = i + 10; 9 } 10 //數組的修改經過下標來修改 11 array[2] = 100; 12 //輸出 13 for (int j = 0; j < array.Length; j++) 14 { 15 Console.WriteLine(array[j]); 16 } 17 18 //模式二:直接賦值 19 string[] array2 = new string[] { "二胖", "二狗" }; 20 }
2. ArrayList(可變長度的數組)
沒必要在聲明的時候指定長度,即長度可變;能夠存放不一樣的類型的元素。
致命缺點:不管什麼類型存到ArrayList中都變爲object類型,使用的時候又被還原成原先的類型,因此它是類型不安全的,當值類型存入的時候,會發生裝箱操做,變爲object引用類型,而使用的時候,又將object類型拆箱,變爲原先的值類型,這尼瑪,你能忍?
結論:不推薦使用,建議使用List代替!!
特別注意:ArrayList不是線程安全,在多線程中須要配合鎖機制來進行。
1 { 2 Console.WriteLine("---------------------------02 ArrayList(可變長度的數組)-----------------------------------"); 3 ArrayList arrayList = new ArrayList(); 4 arrayList.Add("二胖"); 5 arrayList.Add("馬茹"); 6 arrayList.Add(100); 7 for (int i = 0; i < arrayList.Count; i++) 8 { 9 Console.WriteLine(arrayList[i] + "類型爲:" + arrayList[i].GetType()); 10 } 11 }
3. List<T> (泛型集合) 推薦使用
內部採用array實現,但沒有拆箱和裝箱的風險,是類型安全的
特別注意:List<T>不是線程安全,在多線程中須要配合鎖機制來進行,若是不想使用鎖,能夠用ConcurrentBag這個線程安全的數組來替代List<T>
1 { 2 Console.WriteLine("---------------------------03 List<T> (泛型集合)-----------------------------------"); 3 List<string> arrayList = new List<string>(); 4 arrayList.Add("二胖"); 5 arrayList.Add("馬茹"); 6 arrayList.Add("大胖"); 7 //修改操做 8 arrayList[2] = "葛帥"; 9 //刪除操做 10 //arrayList.RemoveAt(0); 11 for (int i = 0; i < arrayList.Count; i++) 12 { 13 Console.WriteLine(arrayList[i]); 14 } 15 }
4. LinkedList<T> 鏈表
在內存空間中存儲的不必定是連續的,因此和數組最大的區別就是,沒法用下標訪問。
優勢:增長刪除快,適用於常常增減節點的狀況。
缺點:沒法用下標訪問,查詢慢,須要從頭挨個找。
特別注意:LinkedList<T>不是線程安全,在多線程中須要配合鎖機制來進行。
{ Console.WriteLine("---------------------------04 ListLink<T> 鏈表-----------------------------------"); LinkedList<string> linkedList = new LinkedList<string>(); linkedList.AddFirst("二胖"); linkedList.AddLast("馬茹"); var node1 = linkedList.Find("二胖"); linkedList.AddAfter(node1, "三胖"); //刪除操做 linkedList.Remove(node1); //查詢操做 foreach (var item in linkedList) { Console.WriteLine(item); } }
5. Queue<T> 隊列
先進先出,入隊(Enqueue)和出隊(Dequeue)兩個操做
特別注意:Queue<T>不是線程安全,在多線程中須要配合鎖機制來進行,若是不想使用鎖,線程安全的隊列爲 ConcurrentQueue。
實際應用場景:利用隊列解決高併發問題(詳見:http://www.cnblogs.com/yaopengfei/p/8322016.html)
1 { 2 Console.WriteLine("---------------------------05 Queue<T> 隊列-----------------------------------"); 3 Queue<int> quereList = new Queue<int>(); 4 //入隊操做 5 for (int i = 0; i < 10; i++) 6 { 7 quereList.Enqueue(i + 100); 8 } 9 //出隊操做 10 while (quereList.Count != 0) 11 { 12 Console.WriteLine(quereList.Dequeue()); 13 } 14 }
6. Stack<T> 棧
後進先出,入棧(push)和出棧(pop)兩個操做
特別注意:Stack<T>不是線程安全
1 { 2 Console.WriteLine("---------------------------06 Stack<T> 棧-----------------------------------"); 3 Stack<int> stackList = new Stack<int>(); 4 //入棧操做 5 for (int i = 0; i < 10; i++) 6 { 7 stackList.Push(i + 100); 8 } 9 //出棧操做 10 while (stackList.Count != 0) 11 { 12 Console.WriteLine(stackList.Pop()); 13 } 14 }
7. Hashtable
典型的空間換時間,存儲數據不能太多,但增刪改查速度很是快。
特別注意:Hashtable是線程安全的,不須要配合鎖使用。
{ Console.WriteLine("---------------------------07 Hashtable-----------------------------------"); Hashtable tableList = new Hashtable(); //存儲 tableList.Add("001", "馬茹"); tableList["002"] = "二胖"; //查詢 foreach (DictionaryEntry item in tableList) { Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString()); } }
8. Dictionary<K,T>字典 (泛型的Hashtable)
增刪改查速度很是快,能夠用來代替實體只有id和另外一個屬性的時候,大幅度提高效率。
特別注意:Dictionary<K,T>不是線程安全,在多線程中須要配合鎖機制來進行,若是不想使用鎖,線程安全的字典爲 ConcurrentDictionary。
1 { 2 Console.WriteLine("---------------------------08 Dictionary<K,T>字典-----------------------------------"); 3 Dictionary<string, string> tableList = new Dictionary<string, string>(); 4 //存儲 5 tableList.Add("001", "馬茹"); 6 tableList.Add("002", "二胖"); 7 tableList["002"] = "三胖"; 8 //查詢 9 foreach (var item in tableList) 10 { 11 Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString()); 12 } 13 }
強調:
以上8種類型,除了Hashtable是線程安全,其他都不是,都須要配合lock鎖來進行,或者採用 ConcurrentXXX來替代。
詳細的請見:http://www.cnblogs.com/yaopengfei/p/8322016.html
二. 四大接口比較
1. IEnumerable
是最基本的一個接口,用於迭代使用,裏面有GetEnumerator方法。
2. ICollection
繼承了IEnumerable接口,主要用於集合,內部有Count屬性表示個數,像ArrayList、List、LinkedList均實現了該接口。
3. IList
繼承了IEnumerable 和 ICollection,實現IList接口的數據接口可使用索引訪問,表示在內存上是連續分配的,好比Array、List。
4. IQueryable
這裏主要和IEnumerable接口進行對比。
Enumerable裏實現方法的參數是Func委託,Queryable裏實現的方法的參數是Expression表達式。
實現IQueryable和IEnumabler均爲延遲加載,但兩者的實現方式不一樣,前者爲迭代器模式,參數爲Func委託,後者爲Expression表達式目錄樹實現。
三. yield關鍵字
1. yield必須出如今IEunmerable中
2. yield是迭代器的狀態機,能作到延遲查詢,使用的時候才查詢,能夠實現按序加載
3. 例子
測試一:在 「var data1 = y.yieldWay();」加一個斷點,發現直接跳過,不能進入yieldWay方法中,而在「foreach (var item in data1)」加一個斷點,第一次遍歷的時候就進入了yieldWay方法中,說明了yield是延遲加載的,只有使用的時候才查詢。
測試二:對yieldWay和commonWay獲取的數據進行遍歷,經過控制檯發現前者是一個一個輸出,然後者是先一次性獲取完,一下所有輸出來,證實了yield能夠作到按需加載,能夠在foreach中加一個限制,好比該數據不知足>100就不輸出。

1 //********************************* 下面爲對比普通返回值和使用yeild返回值的方法 ************************************************ 2 3 /// <summary> 4 /// 含yield返回值的方法 5 /// </summary> 6 /// <returns></returns> 7 public IEnumerable<int> yieldWay() 8 { 9 for (int i = 0; i < 10; i++) 10 { 11 yield return this.Get(i); 12 } 13 } 14 /// <summary> 15 /// 普通方法 16 /// </summary> 17 /// <returns></returns> 18 public IEnumerable<int> commonWay() 19 { 20 int[] intArray = new int[10]; 21 for (int i = 0; i < 10; i++) 22 { 23 intArray[i] = this.Get(i); 24 } 25 return intArray; 26 } 27 28 /// <summary> 29 /// 一個獲取數據的方法 30 /// </summary> 31 /// <param name="num"></param> 32 /// <returns></returns> 33 private int Get(int num) 34 { 35 Thread.Sleep(1000); 36 return num * DateTime.Now.Second; 37 }
1 Console.WriteLine("-----------------------下面是調用yield方法-----------------------"); 2 yieldDemo y = new yieldDemo(); 3 var data1 = y.yieldWay(); 4 foreach (var item in data1) 5 { 6 Console.WriteLine(item); 7 } 8 Console.WriteLine("-----------------------下面是調用普通方法-----------------------"); 9 var data2 = y.commonWay(); 10 foreach (var item in data2) 11 { 12 Console.WriteLine(item); 13 }
各類通信鏈接方式
1. http https (ajax)
2. 總結一下同一個項目下 先後交互的集中提交方式 (.Net特有的 EasyUI封裝的)
3. 跨域請求的幾種形式
3. HttpClient
4. WebService
5. WebAPI(重點)
6. socket
7. websocket (重點)+superSocket
8. SignalR (重點)
9. nodejs中的net通信模塊
10. WCF (能夠棄療了)
設計模式篇
一. 什麼是設計模式
糾結了很久,今天終於下定決心開始寫設計模式系列,由於這個系列章節確實很差寫,在這以前,也看了好多關於設計模式的博客、視頻、書籍等,大多數用的例子要麼貓啊狗啊、大雁等動物類;要麼就是學生類,教師類,吐槽一下,真shit,試想一下,哪一個項目中會用這些動物類教師類呢?
我也和我同行的朋友交流了一下關於設計模式,對設計模式的理解,能夠分爲這麼幾個層次:
①:根本不知道什麼是設計模式。
②:據說過幾種設計模式,理解不深。
③:能寫出並理解幾種設計模式,但不知道在項目中該怎麼用。
毋庸置疑,可否靈活的運用好設計模式,是一個名開發工程師邁向架構師的必經之路,上面說的這麼玄乎,那麼到底什麼是涉及模式呢?這裏先借助金庸的武俠小說來類比一下。
做爲金庸迷的我,金庸老師的「飛雪連天射白鹿,笑書神俠倚碧鴛」14部小說每一部看了都不低於3遍以上,對裏面個各類武功也是瞭如指掌,像效果比較炫麗,威力比較大的有:「喬幫主降龍十八掌、段譽的六脈神劍、楊過的黯然銷魂掌、任我行的吸星大法等等」,這些都是外家功夫,種類不少,一我的可能會多種,這就比如.Net的中MVC、EF、SignalR等等;固然也有內功心法,典型的有:」少林和尚的易筋經、張無忌的九陽神功」,這兩種功夫自己並無太大的殺傷力,但會了這種功夫,更容易融會貫通外家功夫,使外家功夫發揮出更大效果,拿到咱們開發領域,「設計模式」就是內功心法,沒有語言之分,它是一種模式,一種思想指導着咱們開發。
那麼怎麼才能算精通設計模式呢?
看過《倚天屠龍記》的朋友可能會記得裏面有這麼一個場景:趙敏冒充明教挑戰張三丰的時候,張無忌辦成小道童出來救場,在對陣三個家奴的的時候,張三丰教了張無忌一套太極拳法,裏面有這麼一段對話:
張三丰演示完後,問張無忌:「無忌,你記住了多少」,張無忌回答說:「無忌不才,只有一小部分沒有記住」;過了一會,張三丰又問道:「如今能記住多少」,無忌說:「太師傅,我已經所有忘記了」,這時,張三丰說:「無忌你能夠上了」,結果顯然而知,對手被打的那叫一個慘啊。
因此:設計模式的最高境界是,忘記設計模式,將23種的設計模式天然而然的融入開發中,哈哈,固然這個有點難,沒有個五年以上的功力,很難達到這個層次。
二. 設計模式的內容
設計模式是一種套路,是把 「別人成功的例子」 拿過來靈活運用,咱們的優秀的前輩總結出來7個設計原則和23種設計模式。
設計原則:
1. 單一職責原則 2. 里氏替換原則 3. 依賴倒置原則
4. 接口隔離原則 5. 迪米特原則(最小知道原則) 6. 開閉原則
7. 組合聚合原則
設計模式:
1. 建立型模式 :工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
2. 結構型模式:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
3. 行爲型模式:策略模式、模板方法模式、觀察者模式、迭代器模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
解釋:設計模式只是一個大概的套路,不能生搬硬套,有一些作法多是多個設計模式的融合,有的也很難歸爲某個設計模式。不要爲了設計模式而設計模式,沒有十全十美的代碼。
在上述設計模式中,大部分設計模式都用到了上面的設計思想,在設計模式中,咱們會常常看到:開閉原則、里氏替換原則(多態)、面向接口編程、面向抽象編程、抽象類、override覆寫這幾種技術。
若是對於抽象類或者override不明白的,有必要先看一下下面的章節:
第一節:從面向對象思想(oo)開發、接口、抽象類以及兩者比較
第二節:重寫(new)、覆寫(overwrite)、和重載(overload)
三. 系列章節
下面將由淺入深,結合實際案例來說解設計原則和設計模式。
第二節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第三節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第四節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第五節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第六節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第七節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第八節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第九節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十一節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十二節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十三節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十四節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十五節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十六節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十七節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十八節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第十九節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十一節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十二節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十三節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十四節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十五節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十六節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十七節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十八節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第二十九節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第三十節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第三十一節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第三十二節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
第三十三節:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
(進度:一週寫 2-3篇,大約2個半月 完成該系列)
第十二節: 總結Quartz.Net幾種部署模式(IIS、Exe、服務部署【藉助TopSelf、服務類】)
一. IIS部署
好比在MVC框架中集成了Quartz.Net定時調度,此時該調度系統會隨着MVC框架被掛在IIS下,IIS會進程回收,因此大部分開發都會遇到Quartz掛在IIS下一段時間很差用。
補充:IIS能夠設置定時自動回收,默認回收是1740分鐘,也就是29小時。IIS自動回收至關於服務器IIS重啓,應用程序池內存清空,全部數據被清除,至關於IIS重啓,在度量快速開發平臺服務器端,爲了減少數據庫負擔,內存中暫存了不少信息,不適合頻繁的回收,由於回收會形成服務器端全部存在內存中的數據丟失,若是沒有及時保存到數據庫中,可能致使程序出現問題。而若是系統使用高峯時期,並不適合回收,回收可能致使幾十秒IIS無響應,對於正在工做的人員來講,是一種很很差的體驗,會覺得是網絡或者掉線等問題。
解決方案:關閉該項目在IIS上對應的進程池的回收機制。
如何關閉進程池的回收機制:選中IIS中部署的項目對應的進程池,點擊【高級設置】,裏面有5個核心參數:
① 發生配置更改時禁止回收:若是爲True,應用程序池在發生配置更改時將不會回收。
② 固定時間間隔(分鐘):超過設置的時間後,應用程序池回收,設置爲:0 意味着應用程序池不回收。系統默認設置的時間是1740(29小時)。
③ 禁用重疊回收:若是爲true,將發生應用程序池回收,以便在建立另外一個工做進程以前退出現有工做進程
④ 請求限制:應用程序池在回收以前能夠處理的最大請求數。若是值爲0,則表示應用程序池能夠處理的請求數沒有限制。
⑤ 生成回收事件日誌條目:每發生一次指定的回收事件時便產生一個事件日誌條目。
總結:即便能夠將IIS進程池回收關掉,仍然不建議把Quartz掛到IIS下,長時間不回收,會存在內存溢出的問題。
二. C/S程序直接運行
咱們能夠用控制檯的形式或者Winform的形式單獨作一套定時調度系統,與主框架分類,也便於維護,能夠直接將exe程序或者Winform窗體程序在服務器上運行。
總結:該方法不存在回收的問題,但直接在服務器上運行,容易不當心被運維人員關掉了。
對於專業一點的人員來講,該方案,直接運行程序太Low了,因此一般是將exe程序發不成windows服務,經過服務的開啓和關閉來 維護。
三. 藉助topshelf來進行的windows服務部署
官網:http://topshelf-project.com/ , 這是一種通用的發佈服務的方式,步驟以下:
1. 經過NuGet下載 Topshelf 的程序集
2. 配置QuartzService類,充當定時任務的服務端程序
①:構造函數中執行定時調度任務
②:Start()方法控制調度的開啓 (必填)
③:Stop()方法控制調度的關閉 (必填)
④:Pause()方法暫停調度 (選填)
⑤:Continue()方法繼續調度 (選填)
⑥:Shutdown() 關閉 (須要支持)
3. 在主程序中經過topshelf代碼調用: HostFactory.Run 詳見主程序。(在裏面能夠設置服務的名稱、描述等)
4. 經過指令進行服務發佈和卸載(查看windows服務:services.msc)
①:經過cmd命令定位到bin文件目錄下(以管理員身份運行)
(eg: 先輸入 d: 切換到D盤,再執行下面的命令 cd D:\06-個人開發之路\DotNet體系\04-DotNet專題篇\01-Quartz.Net定時調度\01-Code\MyQuarzt\QuartzDemo\bin\Debug )
②:QuartzDemo.exe help: 查看指令
QuartzDemo.exe install: 安裝服務
QuartzDemo.exe install start : 安裝服務且開啓
QuartzDemo.exe uninstall :卸載服務
截止此處,大功告成,能夠看到D盤中多了一個txt文件,每隔3s多一條數據
下面分享整個過程的代碼部署的相應截圖:
(1). Quartz代碼和TopSelf調用代碼:
1 public class QuartzService 2 { 3 IScheduler scheduler = null; 4 /// <summary> 5 /// 定時調度業務 6 /// </summary> 7 public QuartzService() 8 { 9 //1.建立做業調度池(Scheduler) 10 scheduler = StdSchedulerFactory.GetDefaultScheduler(); 11 12 //2.建立一個具體的做業即job (具體的job須要單獨在一個文件中執行) 13 var job = JobBuilder.Create<HelloJob5>().Build(); 14 15 //3.建立並配置一個觸發器即trigger 3s執行一次 16 var trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(3) 17 .RepeatForever()).Build(); 18 //4.將job和trigger加入到做業調度池中 19 scheduler.ScheduleJob(job, trigger); 20 } 21 /// <summary> 22 /// 開啓任務 23 /// </summary> 24 public void Start() 25 { 26 scheduler.Start(); 27 } 28 /// <summary> 29 /// 關閉任務 30 /// </summary> 31 public void Stop() 32 { 33 //true:表示該Sheduler關閉以前須要等如今全部正在運行的工做完成才能關閉 34 //false:表示直接關閉 35 scheduler.Shutdown(true); 36 } 37 /// <summary> 38 /// 暫停調度 39 /// </summary> 40 public void Pause() 41 { 42 scheduler.PauseAll(); 43 } 44 /// <summary> 45 /// 繼續調度 46 /// </summary> 47 public void Continue() 48 { 49 scheduler.ResumeAll(); 50 } 51 52 }
1 HostFactory.Run(x => //1 2 { 3 x.Service<QuartzService>(s => //2 4 { 5 s.ConstructUsing(name => new QuartzService()); //3 6 //開啓和關閉 必選項 7 s.WhenStarted(tc => tc.Start()); //4 8 s.WhenStopped(tc => tc.Stop()); //5 9 10 // optional pause/continue methods if used 11 // 暫停和繼續 選填 12 s.WhenPaused(tc => tc.Pause()); 13 s.WhenContinued(tc => tc.Continue()); 14 15 //// optional, when shutdown is supported 16 //s.WhenShutdown(tc => tc.Shutdown()); 17 18 }); 19 x.RunAsLocalSystem(); //6 20 x.SetDescription("測試藉助TopSelf將Quartz發佈成服務"); //7 21 x.SetDisplayName("QuartzService"); //8 22 x.SetServiceName("QuartzService2"); //9 23 });
(2). 定位到指定路徑,並進行相應的服務部署
a. 不少指令
b. 安裝服務並開啓
查看服務列表:
查看服務運行結果:
刪除服務:
三. 藉助window服務類
這裏不詳細介紹(網上一堆一堆的),推薦採用上面的TopShelf的服務發佈方式。