在.NET Framework 中. System.IO 命名空間主要包含基於文件(和基於內存)的輸入輸出(I/O)服務的相關基礎類庫.和其餘命名空間同樣. System.IO 定義了一系列類、接口、枚舉、結構和委託。
它們大多數包含在 mscorlib.dll! 另外有一部分部分 System.IO 命名空間的成員則包含在systcm.dll程序集中。
System.IO命名空間的多數類型主要用於編程操做物理目錄和文件,而另外一些類型則提供了從字符串緩衝區和內存區域中讀寫數據的方法。php
下面是有關System.IO命名空間的主要成員html
System.IO命名空間的主要成員 | |
非抽象I/O類類型 | 做用 |
BinaryReader和BinaryWriter | 這兩個類型可以以二進制值存儲和讀取基本數據(整型,布爾型,字符串型和其餘類型) |
BufferedStream | 這個類型爲字節流提供了臨時的存儲空間,能夠以之後提交 |
Directory和DirectoryInfo | 這兩個類型用來操做計算機的目錄結構。Directory 類型主要的功能經過靜態方法實現。 DirectoryInfo 類創剛經過一個有效的時象引用來實現相似功能 |
DriveInfo | 提供計算機驅動器的詳細信息 |
File 和 FlleInfo | 這兩個類用來操做計算機上的一組文件。Fi1e類型主要的功能經過靜態成員實現。FlleInfo類型則經過一個有效的對象引用來實現相似功能 |
FileStream | 這個類型實現文件隨記訪問(好比尋址能力)以字節流來表示教據 |
FileSystemWatcher | 這個類型監控對指定外部文件的更改 |
MemoryStream | 這個類型實現對內存(而不是物理文件)中存儲的流教據的隨機訪問 |
Path | 這個類型對包含文件或目錄路徑信息的System.Stream類型執行操做。這些操做是與平臺無關的 |
StreamWriter和StreamReader | 這兩個類型用來在(從)文件中存儲(獲取)文本信息。不支持隨機文件訪問 |
StringWriter和StringReader | 和StreamWriter/StreamReader類型差很少.這兩個類型一樣同文本信息打交道,不一樣的是基層的存儲器是字符串緩衝區而不是物理文件 |
Directory(Info)和File(Info) 類型 實現單個文件和計算機目錄操做mysql
通常說來, Fllelnfo 和DirectoryInfo 是獲取文件或目錄細節(如建立時間、讀寫能力等)更好的方式,由於它們的成員每每會返回強類型的對象。相反,Directory 和File類成員每每會返回簡單字符串值而不是強類型對象。不過.這僅僅是一個準則。在不少狀況下,你均可以使用 File/FileInfo或Directory/DirectoryInfo 完成相同的工做git
Filesystemlnfo 抽象基類github
DirectoryInfo 和 FlleInfo 類型實現了許多FilesystemInfo 抽象基類的行爲。大部分 FllesystemInfo類成員的做用是用來獲取指定文件或目錄的通常特性(好比建立時間、各類特性等)。web
FilesystemInfo 屬性sql
FllesystemInfo 屬性 | |
屬性 | 做用 |
Attributes | 獲取或設置與當前文件關聯的特性.由 FlleAttrlbutes 枚舉表示(例如.是隻讀、加密、隱藏或壓縮文件或目錄) |
CreationTime | 獲取或設置當前文件或目錄的建立時間 |
Exists | 用來判斷指定文件或目錄是否存在的值 |
Extension | 獲取文件的擴展名 |
FullName | 獲取目錄或文件的完整路徑 |
LastAccessTime | 獲取或設置上次訪問當前文件或目錄的時間 |
LastWriteTime | 獲取或設置上次寫人當前文件或目錄的時間 |
Name | Name 獲取當前文件或目錄的名稱 |
FilesystemInfo 類型還定義了Delete()方法,該操做由派生類型從硬盤中刪除指定文件或目錄來實現。一樣,在獲取文件特性前使用Refresh()方法能確保當前文件(或目錄)的統計信息是最新的。數據庫
使用Directoryinfo類型編程
DirectoryInfo類包含一組用來建立、移動,刪除和枚舉全部目錄/子目錄的成員json
DirectoryInfo類型的主要成員 | |
成員 | 操做 |
Create()和CreateSubdirectory() | 按照路徑名創建一個目錄(或者一組子目錄) |
Delete() | 刪除一個目錄和它的全部內容 |
GetDirectories() | 返回一個表示當前目錄中全部子目錄的DirectoryInfo對象數組 |
GetFiles() | 返回Filelnfo對象教組,表示指定目錄下的一組文件 |
MoveTo() | 將一個目錄及其內容移動到一個新的路徑 |
Parent | 獲取指定路徑的父目錄 |
Root | 獲取路徑的根部分 |
獲取DirectoryInfo 的屬性代碼以下
DirectoryInfo dir1 = new DirectoryInfo("."); //綁定當前的應用程序目錄 DirectoryInfo dir2 = new DirectoryInfo(@"D:\360Downloads");//使用存在的目錄 //若是試圖使用一個不存在的目錄.系統會引起System.IO.DirectoryNOtFoundExceptlon 異常。所以,若是指定了一個還沒有建立的目錄的話,在對目錄進行操做前首先須要調用Create()方法。 DirectoryInfo dir3 = new DirectoryInfo(@"D:\360Downloads\dir2"); dir3.Create(); Console.WriteLine("DirectoryInfo 主要成員的實現"); Console.WriteLine("FullName:{0}", dir3.FullName); Console.WriteLine("Name:{0}", dir3.Name); Console.WriteLine("Parent:{0}", dir3.Parent); Console.WriteLine("CreationTime:{0}", dir3.CreationTime); Console.WriteLine("Attributes:{0}", dir3.Attributes); Console.WriteLine("Root:{0}", dir3.Root);
使用DirectoryInfo 類型枚舉出文件
DirectoryInfo dir = new DirectoryInfo(@"C:\Users\Public\Pictures"); FileInfo[] imageFiles = dir.GetFiles("*.jpg", SearchOption.AllDirectories); foreach (FileInfo f in imageFiles) { Console.WriteLine("********************"); Console.WriteLine("file Name:{0}", f.Name); Console.WriteLine("file Length:{0}", f.Length); Console.WriteLine("file CreationTime:{0}", f.CreationTime); Console.WriteLine("file Attributes:{0}", f.Attributes); Console.WriteLine("********************"); }
使用DirectoryInfo類型建立子目錄
DirectoryInfo dir = new DirectoryInfo(@"D:\360Downloads\dir2"); dir.CreateSubdirectory("MyFolder"); //盡骨不必定要去捕獲 CreateSubdirectory()方法的返回值,可是須要知道的是.若是執行成功.它會返回DirectoryInfo類型 DirectoryInfo dir1=dir.CreateSubdirectory(@"MyFolser2\Data"); Console.WriteLine("New Foloser:{0}",dir1);
使用Directory類型
Directory的靜態成員實現了由DirectoryInfo定義的實例級成員的大部分功能。 Directory成員返回的是字符串數據而不是強類型的Filelnfo 和 DirectoryInfo對象。
獲取此計算機上的邏輯驅動器代碼以下:
string[] dives = Directory.GetLogicalDrives(); Console.WriteLine("Here are your drives:"); foreach (string s in dives) { Console.WriteLine("------->{0}", s); }
刪除以前創建的目錄代碼以下
try { Directory.Delete(@"D:\360Downloads\dir2\MyFolser2\Data"); } catch (IOException e) { Console.WriteLine(e.Message); }
使用DriveInfo類類型
System.IO命名空間提供了一個叫作Drivelnfo的類。和Drivelnfo.GetLogicalDrivers()類似.Drlvelnfo.GetDrives()靜態方法能獲取計算機上驅動器的名字。然而和Drlvelnfo.GetDrives()不一樣,Drlvelnfo提供了許多其餘的細節(好比驅動器類型、可用空間、卷標等)。
獲得全部驅動器的信息代碼以下
DriveInfo[] myDivers = DriveInfo.GetDrives(); foreach (DriveInfo d in myDivers) { Console.WriteLine("Name:{0}", d.Name); Console.WriteLine("Type:{0}", d.DriveType); //檢查驅動器是否已經安裝好 if (d.IsReady) { Console.WriteLine("Free space:{0}", d.TotalFreeSpace); Console.WriteLine("Format space:{0}", d.DriveFormat); Console.WriteLine("Lable space:{0}", d.VolumeLabel); } Console.WriteLine("***************************"); }
使用FileInfo類類型
FlleInfo 類能讓咱們得到硬盤上現有文件的詳細信息(建立時間,大小、文件特性等),並幫助咱們建立、複製、移動和刪除除文件
刪除FileInfo實例綁定的文件
FileInfo 核心成員 | |
成員 | 做用 |
AppendText() | 建立一個StreamWriter類型(後面會討論),它用來向文件追加文本 |
CopyTo() | 將現有文件複製到新文件 |
Create() | 建立一個新文件而且返回一個FileStream類型(後面會討論).經過它來和新建立的文件進行交互 |
CreateText() | 建立一個寫入新文本文件的StreamWriter對象 |
Delete() | 刪除FileInfo實例綁定的文件 |
Directory | 獲取父目錄的實例 |
DirectoryName | 獲取父目錄的完整路徑 |
Length | 獲取當前文件的大小 |
MoveTo() | 將指定文件移到新位置.井提供指定新文件名的選項 |
Name | 獲取文件名 |
Open() | 用各類讀/寫訪問權限和共享特權打開文件 |
OpenRead() | 建立只讀FileStream對象 |
OpenText() | 建立從現有文本文件中讀取教據的StreamReade(後面會討論) |
OpenWrite() | 建立只寫FileStream類型 |
注意,大部分FileInfo類的成員返回I/O相關的特定對象(Filestream和StreamWriter),讓咱們以不一樣格式從關聯文件讀或向關聯文件寫數據。
FileInfo.Create()的用法
1
2
3
|
FileInfo f =
new
FileInfo(
@"D:\360Downloads\dir2\Test.txt"
);
FileStream fs = f.Create();
fs.Close();
|
須要注意的是,FlleInfo.Create()方法返一個FileStearm對象。FileStream能對基層的文件進行同步/異步的讀寫操做:須要知道的是,FileInfo.Create()返回的FileStream對象給全部的用戶授予徹底讀寫操做權限.
還要注意,在使用了當前FileStream對象以後.要確保關閉句柄來釋放流的底層非託管資源。因爲FileStream實現了IDisposable,因此咱們可使用C#的using域來讓編譯器生成釋放邏輯
FileInfo f1 = new FileInfo(@"D:\360Downloads\dir2\Test2.txt"); using (FileStream f2=f1.Create()) { }
FileInfo.Open() 方法
咱們能使用FileInfo.Open()方法來打開現有文件.同時也能使用它來建立新文件,它比FileInfo.Create()多了不少細竹.由於open()一般有好幾個參數,能夠限定所操做的文件的總體結構。一旦調用open()完成後.它返回一個FileStream對象。
FileInfo f = new FileInfo(@"D:\360Downloads\dir2\Test3.txt"); using (FileStream fs = f.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) { }
* 上面的重載 OPen()方法須要3個參數。
* 第一個參數指定I/O請求的基本方式(好比說新建文件、打開現有文件和追加文件等),它的值由FileMode
* 第二個參數的值由FileAccess枚舉定義,用來決定基層流的讀寫行爲
* 第三個參數FileShare指定文件在其餘文件處理程序中的共享方式。
FileInfo.OpenRead() 和FileInfo.OpenWrite()
FileInfo.Open()方法能讓咱們用很是靈活的方式獲取文件句柄,FileInfo類一樣提供了OpenRead()和OpenWrite()成員。這些方法不須要提供各類枚舉值.就能返回一個正確配置的只讀或只寫的FileStream類型。 OPenRead()和OPenWrite()也都返回一個FileStream對象
FileInfo f = new FileInfo(@"D:\360Downloads\dir2\test.txt"); using (FileStream fs = f.OpenRead()) { Console.WriteLine("ok"); } FileInfo f4 = new FileInfo(@"D:\360Downloads\dir2\test4.txt"); using (FileStream fs1 = f4.OpenWrite()) { }
FileInfo.OpenText()
OpenText()方法返回的是一個StreamReader類型(而不是否是FileStream類型)的實例。
FileInfo f = new FileInfo(@"D:\360Downloads\dir2\Test3.txt"); using (StreamReader reader = f.OpenText()) { }
FileInfo.CreateText() 和 FileInfo.AppendText()
FileInfo.CreateText() 和 FileInfo.AppendText()都返回的是一個StreamWriter類型
FileInfo f = new FileInfo(@"D:\360Downloads\dir2\Test6.txt"); using (StreamWriter sr = f.CreateText()) { } FileInfo f1 = new FileInfo(@"D:\360Downloads\dir2\aa.txt"); using (StreamWriter sr = f1.AppendText()) { }
使用 File類型
Flle類型的靜態成員提供了和FileInfo類型差很少的功能。與FileInfo相似,File類提供了
AppendText(),create ()、 createText ()、 open()、 OPenRead ()、 openWrite()和 OpenText()方法。在大多數狀況下,File 和 FileInfo 類型能互換使用。
using (FileStream fs = File.Create(@"D:\360Downloads\dir2\bb.txt")) { } using (FileStream fs = File.Open(@"D:\360Downloads\dir2\bb.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) { } using (FileStream fs = File.OpenRead(@"D:\360Downloads\dir2\bb.txt")) { } using (FileStream fs = File.OpenWrite(@"D:\360Downloads\dir2\bb.txt")) { } using (StreamReader sr = File.OpenText(@"D:\360Downloads\dir2\bb.txt")) { } using (StreamWriter sw = File.CreateText(@"D:\360Downloads\dir2\bb.txt")) { } using (StreamWriter sw = File.AppendText(@"D:\360Downloads\dir2\bb.txt")) { }
使用File 類型的這些新方法,只用幾行代碼就能夠批量讀寫數據。更好的是,每個成員都自動關閉基層文件句柄
File新方法以下
方法 | 做用 |
ReadAllBytes() | 打開指定文件,以字節數組形式返回二進制數據,而後關閉文件 |
ReadAllLines() | 打開指定文件,以字符串教組形式返回字符教據.而後關閉文件 |
ReadAllText() | 打開指定文件.以System.String形式返回字符數據.而後關閉文件 |
WriteAllBytes() | 打開指定文件.寫人字節數組.而後關閉文件 |
WriteAllLines() | 打開指定文件.寫人字符串教組,而後關閉文件 |
WriteAllText() | 打開指定文件.寫人字符數據.而後關閉文件 |
舉例以下
Console.WriteLine("批量讀寫數據"); string[] myTasks = { "蒸甜糕", "作糖餅", "醃桂花蜜", "作桂花釀" }; //寫入文件 File.WriteAllLines(@"D:\360Downloads\dir2\bb.txt", myTasks); //從新讀取而後輸出 foreach (string item in File.ReadAllLines(@"D:\360Downloads\dir2\bb.txt")) { Console.WriteLine(item); }
Stream抽象類
在 I/ O 操做中,流表明了在源文件和目標文件之間傳輸的必定量的數據。不管使用什麼設備(文件、網絡鏈接和打印機等)存儲或者顯示字節。」流」都能提供一種通用的方式來和字節隊列進行交互
「流」的概念不只僅侷限於文件輸入/輸出. NET類庫提供了」流」來訪問網絡、內存地址和其餘一些與流相關的抽象設備
Stream派生類型把數據表現爲原始的字節流。所以,使用原始的Stream類型有點模糊。一些從Stream派生的類型支持尋址(指獲取和調整當前在流中位置的過程)。
下面有關 抽象Stream成員
抽象Stream成員 | |
成員 | 做用 |
CanRead,CanWrite和CanSeek | 檢側當前流是否支持讀、尋址和寫 |
Close() | 關閉當前流並釋放與之關聯的全部資源(如套接字和文件句柄)。在內部,這個方法是Dispose()方法的別名.所以"關閉流"從功能上說等價於"釋放流" |
Flush() | 使用當前的緩衝狀態更新基層的數據源或儲存庫。若是流不實現緩衝.這個方法什麼都不作 |
Length | 返回流的長度,以字節來表示 |
Position | 檢側在當前流中的位置 |
Read()和ReadByte() | 從當前流讀取字節序列或一個字節。井將此流中的位置偏移讀取的字節數 |
Seek | 設置當前流中的位置 |
SeekLength() | 設置當前流的長度 |
Write()和WriteByte() | 向當前流中寫人字節序列或一個字節,將此流中的當前位置偏移寫人的字節數 |
使用FileStream
Fi1eStream類以合適的方式爲基於文件的流提供了抽象Stream成員的實現。這是一個至關原始的流,它只能讀取或寫人一個字節或者字節數組。其實,咱們一般不須要直接和 FileStream類型的成員交互,而是使用各類Stream包裝類,它們能更方便地處理文本數據和.NET 類型。
具體使用以下
Console.WriteLine("***FileStream***"); //獲取一個FileStream對象 using (FileStream fs = File.Open(@"D:\360Downloads\dir2\cc.txt", FileMode.OpenOrCreate)) { string msg = "Hello"; //把字符串編碼成字節數組 byte[] msgAsByteArray = Encoding.Default.GetBytes(msg); //寫入文件 fs.Write(msgAsByteArray, 0, msgAsByteArray.Length); //重置流的內部位置 fs.Position = 0; //從文件讀取字節並顯示在控制檯 Console.WriteLine("**Your Message**"); byte[] bytesFromFile = new byte[msgAsByteArray.Length]; for (int i = 0; i < msgAsByteArray.Length; i++) { bytesFromFile[i] = (byte)fs.ReadByte(); Console.WriteLine(bytesFromFile[i]); } //解碼後字符串 Console.WriteLine("Decoded Messges:"); Console.WriteLine(Encoding.Default.GetString(bytesFromFile)); }
而FileStream的缺點:須要操做原始字節
使用streamwriter和streamreader類型
當須要讀寫基於字符的數據(好比字符串)的時候. streamWriter 和 streamReader 類就很是有用了。它們都默認使用Unicode字符.固然咱們也能夠提供一個正確配置的System.Text.Encoding 對象引用用來改變默認配置。
StreaoReader 和相關的 StringReader類型都從 TextReader 抽象類派生
StreaoWriter 和相關的 StringWriter類型都從 TextWriter 抽象類派生
寫入文本文件(TextWriter )
下面是關於 TextWriter核心成員
TextWriter核心成員 | |
成員 | 做用 |
Close() | 關閉當前編寫器並釋聽任何與該編寫器關聯的系統資源。在這個過程當中.緩衝區將被自動清理(這個成員在功能上等同與調用Dispose()方法) |
Flush() | 清理當的編寫器的全部緩衝試.使全部緩衝數據寫人基礎設備.可是不關閉偏寫器 |
NewLine | 表明派生的編寫器類的行結束符字符串。默認行結束符字符串是回車符後接一個換行符(\r\n) |
Write() | 這個重載的方法將一行寫入文本流.不限行結束符 |
WriteLine() | 這個重載的方法將一行寫入文本流,後跟行結束符 |
派生的StreamWriter類提供了對Write(), close ()和Flush()方法的有效實現.並且還定義了
AutoFlush 屬性。若是把這個屬性設置爲 true 的話, StreamWriter 會在每次執行一個寫操做後,當即寫入數據並清理緩衝區。設置 AutoFlush爲 false 能得到更好的性能,這樣的話.使用 StreamWriter完成寫操做後須要調用 Close()。
寫入文本文件代碼以下:
Console.WriteLine("****Fun with StreamWriter*****"); using (StreamWriter writer = File.CreateText(@"D:\360Downloads\dir2\re.txt")) { writer.WriteLine("魚是作給你們夥兒吃的,喬初薰發覺這些人都挺愛吃甜口吃食,便打算作個糖醋鯉"); writer.WriteLine("魚是作給你們夥兒吃的,喬初薰發覺這些人都挺愛吃甜口吃食,便打算作個糖醋鯉"); writer.WriteLine("魚是作給你們夥兒吃的,喬初薰發覺這些人都挺愛吃甜口吃食,便打算作個糖醋鯉"); for (int i = 0; i < 10; i++) { writer.Write(i + " "); } } Console.WriteLine("Created file");
讀文本文件(TextReader )
下面是關於 TextReader 核心成員
TextReader 主要成員 | |
Peek() | 返回下一個可用字符,而不更改讀取器位置。返回-l表示已經到了流的尾部 |
Read() | 從輸人流中讀取救據 |
ReadBlock() | 從當前流中讀取最大計數字符,並從索引開始將該數據寫人緩衝區 |
ReadLine() | 從當前流中讀取一行字符,並將數據做爲字符串返回(返回空字符串表明EOF) |
ReadToEnd | 讀取從當前位置到流結尾的全部字符,並將它們做爲一個字符串返回 |
讀文本文件代碼以下:
Console.WriteLine("***Fun with StreamReader***"); using (StreamReader sr = File.OpenText(@"D:\360Downloads\dir2\re.txt")) { string inpt = null; while ((inpt=sr.ReadLine())!=null) { Console.WriteLine(inpt); } }
直接建立streamwriter/streamreader類型
Console.WriteLine("直接建立streamwriter/streamreader類型"); using (StreamWriter sw = File.CreateText(@"D:\360Downloads\dir2\chuchu.txt")) { string mag = " 不一下子,大鍋裏的水燒的滾沸,喬初薰把手洗乾淨,伸手捏了些羊肉,攢成丸子便往鍋裏一丟,小桃兒在旁邊看着直咋舌:「初薰姐姐,這樣會不會太鬆了。」她見喬初薰手上也不怎麼使勁兒,生怕羊肉丸子一進鍋便散了。"; sw.WriteLine(mag); Console.WriteLine("ok"); } using (StreamReader sr = File.OpenText(@"D:\360Downloads\dir2\chuchu.txt")) { string input = null; while ((input = sr.ReadLine()) != null) { Console.WriteLine(input); } }
使用stringwriter和stringreader類型
使用 StringWriter 和 stringReader 類型,咱們能夠將文本信息當作內存中的字符同樣來處理。當想爲基層緩衝區添加基於字符的信息的時候,它們就很是有用。
Console.WriteLine("Fun StringWriter"); using (StringWriter sw = new StringWriter()) { sw.WriteLine("喬初薰手上動做不停,一邊笑着解釋道:「不會。手勁兒太大了反而很差。汆出來的丸子容易發死,吃起來不夠鮮嫩."); Console.WriteLine(sw); }
由於StringWriter 和 StreamWriter 都從一個基類(TextWriter )派生.它們的寫操做邏輯代碼或多或少有點相同。但須要知道, StringWriter 還有一個特色.那就是它能經過 GetStringBuilder() 方法來
獲取一個System.Text.StringBuilder 對象:
Console.WriteLine("GetStringBuilder"); using (StringWriter sw = new StringWriter()) { sw.WriteLine("很快,一邊蒸鍋裏的魚也差很少到火候了。喬初薰拿着勺子攪了攪湯,讓小桃兒把魚端出來。"); Console.WriteLine(sw); StringBuilder sb = sw.GetStringBuilder(); sb.Insert(0, "Hey"); Console.WriteLine(sb.ToString()); sb.Remove(0, "Hey".Length); Console.WriteLine(sb.ToString()); }
使用binarywriter和binaryreader
BinaryWriter和binaryreader都從 System.Object 直接派生。這些類可讓咱們從基層流中以簡潔的二進制格式讀取或寫人離散數據類型。 BinaryWriter 類型定義了一個屢次重載的Write方法,用於把數據類創寫入基層的流,除Write()方法, BinaryWriter還提供了另一些成員讓咱們能獲取或設置從Stream派生的類型,而且提供了隨機數據訪問
下面有關 BinaryWriter核心成員
BinaryWriter核心成員 | |
BaseStream | 這個只讀屬性提供了BinaryWriter對象使用的基層流的訪問 |
Close() | 這個方法關閉二進制流 |
Flush() | 這個方法別新二進制流 |
seek() | 這個方法設置當前流的位置 |
Write | 這個方法將值寫入當前流 |
下面有關 BinaryReaer核心成員
BinaryReader成員 | |
BaseStream | 這個只讀屬性提供了BinaryReder對象使用的基層流的訪問 |
Close() | 這個方法關閉二進制閱讀器 |
PeekChar() | 這個方法返回下一個可用的字符,而且不改變指向當前字節或字符的指針位置 |
Read() | 讀取給定的字節或字符,並把它們存入數組 |
Readxxx() | BinaryReader類定義了許多 Read()方法來從流中獲取下一個類型(ReadBoolean() ReadByte() ReadInt32()) |
下面是有關的代碼
Console.WriteLine("*****Fun with Writer 和Reader******"); FileInfo f = new FileInfo(@"D:\360Downloads\dir2\aa.txt"); using (BinaryWriter bw = new BinaryWriter(f.OpenWrite())) { Console.WriteLine(bw.BaseStream); double a = 1234.67; int i = 3452; string str = "Hello"; bw.Write(a); bw.Write(i); bw.Write(str); } Console.WriteLine("Done!"); using (BinaryReader br = new BinaryReader(f.OpenRead())) { Console.WriteLine(br.ReadDouble()); Console.WriteLine(br.ReadInt32()); Console.WriteLine(br.ReadString()); }
以編程方式"觀察"文件 FileSystemWatcher
Console.WriteLine("****FileSystemWatcher****"); FileSystemWatcher watcher = new FileSystemWatcher(); try { watcher.Path = @"D:\360Downloads\dir2"; } catch (Exception e) { Console.WriteLine(e.Message); } watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Filter = "*.txt"; watcher.Changed += new FileSystemEventHandler(OnChanged); watcher.Created += new FileSystemEventHandler(OnChanged); watcher.Deleted += new FileSystemEventHandler(OnChanged); watcher.Renamed += new RenamedEventHandler(OnRenamed); watcher.EnableRaisingEvents = true; Console.WriteLine("Press q"); while (Console.ReadLine() != "q") static void OnChanged(object source, FileSystemEventArgs e) { Console.WriteLine("File:{0}{1}", e.FullPath, e.ChangeType); } static void OnRenamed(object source, FileSystemEventArgs e) { Console.WriteLine("File:{0},OnRenamed:{1}", e.FullPath, e.ChangeType); }
RabbitMQ安裝筆記
前言
項目中某些場景考慮到高併發狀況,調研後決定使用RabbitMQ,原本覺得很簡單,沒想到配置環境花費了好多時間,按照網上的方法來,老是有其餘問題須要繼續查找,特記錄此筆記,方便下次部署安裝。
本筆記只記錄安裝過程,不探討RabbitMQ技術。
準備
使用RabbitMQ,除了要安裝RabbitMQ外,還必須安裝Erlang,因爲RabbitMQ使用Erlang語言編寫,因此必須先安裝Erlang語言運行環境。
1.Erlang下載:http://www.erlang.org/downloads
2.RabbitMQ下載:http://www.rabbitmq.com/
另外先提一下,Erlang和RabbitMQ安裝好須要添加到系統的環境變量中,由於這個花費了我一些時間。
Erlang安裝
1.安裝
下載完Erlang以後,打開安裝包後,一步步安裝完成,這裏沒什麼須要注意的地方。
2.設置環境變量
找到Erlang的安裝路徑打開到bin文件夾下,注意此路徑。‘
右鍵個人電腦,選擇屬性,找到高級系統設置,點開後選擇高級裏的環境變量。
找到系統變量中的Path,點擊編輯
新建環境變量,添加路徑,能夠直接複製打開的Erlang的bin路徑。
3.檢查Erlang是否安裝成功
打開cmd,輸入 erl 後回車,若是能顯示版本信息,代表安裝成功。
RabbitMQ安裝
1.解壓下載好的RabbitMQ
2.配置環境變量
找到RabbitMQ的安裝路徑打開到sbin文件夾下,注意此路徑。
同Erlang的環境變量添加,新建路徑,添加到系統變量的Path中
3.CMD中安裝、啓動服務
使RabbitMQ以Windows Service的方式在後臺運行,打開CMD,定位到RabbitMQ的sbin目錄下。
執行:
rabbitmq-service install rabbitmq-service enable rabbitmq-service start
注意,CMD須要管理員權限,不然會報錯Unable to register service with service manager. Error: Access is denied.
執行完成後,顯示以下信息,表示RabbitMQ的服務端已經啓動起來了。
而後,能夠用rabbitmqctl這個腳本查看和控制RabbitMQ服務端的狀態。
查看狀態:
rabbitmqctl status
此時查看通常會出現以下錯誤:
解決方法以下:
將C:\Users\XXXX\.erlang.cookie 文件拷貝到C:\Windows\System32\config\systemprofile替換掉.erlang.cookie文件。(XXXX爲window帳戶)
重啓rabbitMQ服務:CMD中 先輸入 net stop RabbitMQ ,而後輸入 net start RabbitMQ
net stop RabbitMQ net start RabbitMQ
此後,再次輸入
rabbitmqctl status
顯示以下信息,RabbitMQ至此已經正確安裝完畢。
用戶設置與web管理工具插件安裝
1.新建用戶
使用命令查看用戶:
rabbitmqctl list_users
RabbitMQ會爲咱們建立默認的用戶名guest和密碼guest,guest默認擁有RabbitMQ的全部權限。
若是咱們須要本身建立用戶,那麼須要執行相似於下面的命令,設置密碼,並授予權限,並將其設置爲管理員。
1 rabbitmqctl add_user ading 123456 //建立用戶ading密碼爲123456 2 rabbitmqctl set_permissions ading ".*" ".*" ".*" //賦予ading讀寫全部消息隊列的權限 3 rabbitmqctl set_user_tags ading administrator //分配用戶組
另,修改用戶密碼和刪除用戶方法以下:
1 rabbitmqctl change_password ading 123 2 rabbitmqctl delete_user ading
2.安裝web管理工具插件
CMD中輸入:
rabbitmq-plugins enable rabbitmq_management
web管理工具的地址是:http://localhost:15672,初始用戶名:guest 初始密碼:guest
打開瀏覽器,輸入地址:http://127.0.0.1:15672/#/
總結
不懂的技術,若是配置部署麻煩,也請不要煩躁,由於他人都得經歷這些。你得知道,那麼多人推薦那麼多使用,老是有緣由的,別由於一點麻煩就放棄一項好的技術。
一次線上問題引起的對於C#中相等判斷的思考
線上報來一個問題,說用戶的數據丟失了。開發通過緊張的調查。終於找到了緣由。
if (newData.GetValue(rowIndex) == oldData.GetValue(rowIndex)) { .................. } public object GetValue(string fieldName)) { ...............
return values[filedName]; //這是一個簡單類型: int,string }
問題出在了 if 中的比較上。 values[rowIndex] 中保存的是一個整數,開發認爲兩個整數比較實用 == 就能夠了。
可是 values[rowIndex] 中的整數通過 GetValue返回後被做爲 object 對象返回了,這時若是還使用 == 進行比較就會出現不等的狀況。
咱們來看一個更全面的例子:
static void Main(string[] args) { object value1 = new object(); object value2 = new object(); value1 = 2; value2 = 2; Console.WriteLine("value1 == value2 {0}", (value1 == value2).ToString()); Console.WriteLine("vvalue1.Equals(value2) {0}", value1.Equals(value2).ToString()); Console.WriteLine("Equals(value1, value2) {0}", Equals(value1, value2).ToString()); Console.WriteLine("ReferenceEquals(value1,value2) {0}", ReferenceEquals(value1,value2).ToString()); }
運行結果
value1 == value2 False
value1.Equals(value2) True
Equals(value1, value2) True
ReferenceEquals(value1,value2) False
若是咱們將value1, value2 都定義爲數字,可是一個是long,一個是uint.
static void Main(string[] args) { long value1 = 2; int value2 = 2; Console.WriteLine("value1 == value2 {0}", (value1 == value2).ToString()); Console.WriteLine("value1.Equals(value2) {0}", value1.Equals(value2).ToString()); Console.WriteLine("Equals(value1, value2) {0}", Equals(value1, value2).ToString()); Console.WriteLine("ReferenceEquals(value1,value2) {0}", ReferenceEquals(value1,value2).ToString()); }
看一下運行結果 ,使用 == ,和 value1.Equals 方法比較是相等的。
value1 == value2 True value1.Equals(value2) True Equals(value1, value2) False ReferenceEquals(value1,value2) False
結合上面兩個例子,咱們定義一個long 變量, 一個unit 變量, 給它們賦值以後,再將這兩個變量賦值給兩個object 對象。
static void Main(string[] args) { object value1 = new object(); object value2 = new object(); long lgval = 2; int ival = 2; value1 = lgval; value2 = ival; Console.WriteLine("lgval == ival {0}", (lgval == ival).ToString()); Console.WriteLine("value1 == value2 {0}", (value1 == value2).ToString()); Console.WriteLine("value1.Equals(value2) {0}", value1.Equals(value2).ToString()); Console.WriteLine("Equals(value1, value2) {0}", Equals(value1, value2).ToString()); Console.WriteLine("ReferenceEquals(value1,value2) {0}", ReferenceEquals(value1,value2).ToString()); }
能夠看到,除去值類型 lgval 和 uval 相等外,其它都是不相等的。
lgval == uval True value1 == value2 False value1.Equals(value2) False Equals(value1, value2) False ReferenceEquals(value1,value2) False
是否是很抓狂? 到底什麼狀況下相等?什麼狀況下不等?咱們先將上面的結果總結一下。
value1 和value2都是Object 對象 含有相同類型的值對象(int) 含有相同的值 |
value1 是long,value2 是 int 含有相同的值 |
value1 和value2都是Object 對象 含有不一樣類型的值對象(long,int) 含有相同的值 |
|
value1 == value2 |
false | true | false |
value1.Equals(value2) |
true | true | false |
Equals(value1, value2) |
true | false | false |
ReferenceEquals(value1,value2) |
false | false | false |
若是將一個值類型賦值給一個object 對象後,如何判斷相等? 微軟官方也沒有給出一個標準的說法。從測試的角度來看。
兩個比較的 object 中的內容若是類型相同,可使用Equals 來進行比較。
不過我我的仍是建議若是是比較值,仍是轉換爲對應的值類型進行比較,這樣比較清晰,不容易犯錯,你們也不用搞清楚 == 和 Equals 以前的細微差異。
ps: 若是object 的類型是 string , 上面的結果又會有所不一樣,有興趣的同窗能夠本身嘗試一下。
ef和mysql使用(一)
ef開發模式有3種:DateBase First(數據庫優先)、Model First(模型優先)和Code First(代碼優先)。這裏我用的是code first 一個簡單的例子:
public class BloggingContext : DbContext { public BloggingContext() : base("name=testConn") { } public DbSet<Person> Blogs { get; set; } public DbSet<DepartPerson> Posts { get; set; } } class Program { static void Main(string[] args) {//模型改變從新建立數據庫 Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BloggingContext>()); using (var db = new BloggingContext()) { Person blog = new Person() { Name = "zhangsan", Age = "29", ID = Guid.NewGuid().ToString().Replace("-", "") }; db.Blogs.Add(blog); db.SaveChanges(); foreach (var item in db.Blogs) { Console.WriteLine("Name:" + item.Name); } } Console.Read(); }
簡單介紹一下Database.SetInitializer方法
一:數據庫不存在時從新建立數據庫
三:模型更改時從新建立數據庫
四:從不建立數據庫
這對這幾種方式,能夠經過代碼作出改動,而後調試一下觀察一下數據庫的變化,會理解的更透徹!
ASP.NET/MVC/Core的HTTP請求流程
ASP.NET HTTP管道(Pipeline)模型
1. 先講一點,再深入思考
通常咱們都在寫業務代碼,優化頁面,優化邏輯之間內徘徊。也許咱們懂得HTTP,HTTPS的GET,POST,可是咱們大部分人是不知道ASP是如何去解析HTTP,或者IIS是如何去處理頁面請求。咱們只知道WebForm拉控件,MVC寫Controller,Action,殊不知道IIS,NetFrameWork幫咱們作了不少事情。那接下咱們就是要去了解IIS幫咱們作了些啥事情。
2. 那啥叫管道(Pipeline)模型
-
初理解Pipeline
-
從一個現象提及,有一家咖啡吧生意特別好,天天來的客人絡繹不絕,客人A來到櫃檯,客人B緊隨其後,客人C排在客人B後面,客人D排在客人C後面,客人E排在客人D後面,一直排到店面門外。老闆和三個員工首先爲客人A準備食物:員工甲拿了一個乾淨的盤子,而後員工乙在盤子裏裝上薯條,員工丙再在盤子裏放上豌豆,老闆最後配上一杯飲料,完成對客人A的服務,送走客人A,下一位客人B開始被服務。而後員工甲又拿了一個乾淨的盤子,員工乙又裝薯條,員工丙又放豌豆,老闆又配上了一杯飲料,送走客人B,客人C開始被服務。一直重複下去。
-
從效率方面觀察這個現象,當服務客人A時,在員工甲拿了一個盤子後,員工甲一直處於空閒狀態,直到送走客人A,客人B被服務。老闆天然而然的就會想到若是每一個人都不停的幹活,就能夠服務更多的客人,賺到更多的錢。老闆經過不停的嘗試想出了一個辦法。以客戶A,B爲例闡述這個方法:員工甲爲客戶A準備好了盤子後,在員工乙開始爲客戶A裝薯條的同時,員工甲開始爲客戶B準備托盤。這樣員工甲就能夠不停的進行生產。整個過程以下圖,客戶們圍着咖啡吧檯排隊,由於有四個生產者,一個老闆加三個員工,因此能夠同時服務四個客戶。咱們將目光轉向老闆,單位時間從他那裏出去的客戶數提升了將近四倍,也就是說效率提升將近四倍。
這樣子,咱們就很好理解了Pipeline了,那其實就是每一個人絡繹不絕的作本身的事情,但我的事情又是整個流程的一部分,有點像工廠的小妹,只作包鞋底,可是這又是製造鞋的中間流程。一條流水,一條管道,一直處理下去。那每一個HTTP請求,到了IIS也是這樣子的。
-
- 針對Pipeline模型帶來的啓示,好處
- 工做流的參考模型
其實就是上面所說同樣,pipeline模型與工做流模型,都是鏈式的,就像一條生產線,各個組件的先後協同。 - 服務framework的參考構建模型
Pipeline模型的一個特色是,在其內部是以組建的模式來實現組合的(雖然這些組建有前後順序之分),它假如你把側重點放在它的鏈式組合上,而不是將側重點放在上面的工做流上(工做流的關注點是流程的前後順序),那麼徹底能夠用這種方式來搭建一個複雜的服務,固然這樣作的前提是,單個組件的粒度必須儘量小而且耦合性極低。
那其實就很像如今的微服務了,只不過微服務更大,服務對象是一個應用程序。
- 工做流的參考模型
-
Pipeline模型的缺點
每次它對於一個輸入(或者一次請求)都必須從鏈頭開始遍歷(參考Http Server處理請求就能明確),這確實存在必定的性能損耗。
3. 講完Pipeline,咱們講他在IIS上的實現
3.1 Http請求帶服務器的時候
-
當服務器接收到一個Http請求的時候,IIS是如何去決定,並處理該請求。答案就是文件的「後綴名」。
服務器獲取所請求的頁面或文件的後綴名後,那服務器就回去尋找能出來這些後綴的應用程序。如果IIS找不到,而且文件沒有受到IIS的保護(保護:App_Code文件夾的文件,不保護:日常的JS文件等),就會直接返回給客戶端(瀏覽器,移動端等)
那麼能處理後綴名的程序叫什麼呢? ISAPI 應用程序(Internet Server Application Programe Interface,互聯網服務器應用程序接口)。它實際上是一個接口,起到一個代理的做用,他的主要工做就是將請求的頁面(文件)與處理該頁面(文件)的後綴的處理程序進行一個映射。讓其能夠爭取的去處理頁面(文件)。
那這個應用程序長什麼樣子呢?- 咱們打開IIS(server2003),隨意選中一個站點,鼠標右擊屬性,而後選擇「主目錄」選項,接着選擇「配置」。而後就能夠看到下面的畫面。
這邊咱們能看到後綴名與之可執行的文件路徑。
接着咱們找到「.axpx」的後綴名,打開來看。咱們能夠發現".aspx"的後綴名是有aspnet_isapi.dll來處理的,因此IIS將".aspx"頁面的請求交給這個dll,就不關心後面是如何處理了。因此咱們如今知道了,ASP.NET只是服務器(IIS)的一個組成部分而已,它是一個ISAPI擴展。
-
上面是server2003的狀況,如今08以上變成了不是在屬性裏面了, 他變成了IIS中的「ISAPI篩選器」和「處理程序映射」了。
- 咱們點擊「ISAPI篩選器」,能夠出現發現,篩選器是分FrameWork與位數的,因此有四個。
- 咱們進入處理程序映射,找出後綴爲「.aspx」的,能夠發現有不少個,對應了framework與位數,而且有個處理程序,
3.雙擊進入,能夠看到能夠處處理模塊的具體路徑,以及請求限制。
注意,這邊要是限制後,頁面(文件)只能以某種特定方式訪問。
3.2 宿主環境(Hosting)
從本質上講,Asp.Net 主要是由一系列的類組成,這些類的主要目的就是將 Http 請求轉變爲對客戶端的響應。HttpRuntime 類是 Asp.Net 的一個主要入口,它有一個稱做 ProcessRequest的方法,這個方法以一個 HttpWorkerRequest 類做爲參數。HttpRuntime 類幾乎包含着關於單個 Http 請求的全部信息:所請求的文件、服務器端變量、QueryString、Http 頭信息 等等。Asp.Net 使用這些信息來加載、運行正確的文件,而且將這個請求轉換到輸出流中,通常來講,也就是 HTML 頁面。固然也但是是文件。
當頁面(文件)發生變更時,爲了可以卸載運行在同一進程中的應用程序,固然卸載也是爲了從新新加載,Http請求被分放在相互隔離的應用程序域。也就是「AppDomain」。
那麼對於IIS來講,IIS依賴一個叫HTTP.SYS的內置驅動程序來監聽外部的HTTP請求,在操做系統啓動的時候,IIS會在HTTP.SYS中註冊本身的虛擬路徑。(註冊能夠理解爲,告訴HTTP.SYS哪些URL能夠訪問,哪些不能夠訪問,404就能夠怎麼來的,就是這麼來的。)
若是是能夠訪問的URL,那麼HTTP.SYS會將這個請求扔給IIS工做者進程。(就是咱們所熟悉的W3WP.EXE,IIS5.0叫作ASPNET_WP.EXE)。
運行是每一個進程都有一個身份標識,以及一系列的可選性能參數,其實就是應用程序池,惟一標識就是應用程序池名稱。
基礎的知識點都懂了,咱們來看下一個大概的HTTP請求的過程
- HTTP.SYS接收Http請求信息。並將信息保存到HttpWorkRequest類中。
- 從相互隔離的AppDomain中加載HttPRuntime。
- 調用HttpRuntime的ProcessRequest方法。
- 程序猿創造世界
- IIS接收返回的數據流,從新返回給HTTP.SSY
- HTTP.SYS將數據返回給客戶端(瀏覽器)
3.3 接下來是重點,理解Pipeline
前面講了那麼多,終於進入到重點,劃重點。
首先咱們先知道了什麼是Pipeline,接着咱們知道一個Http的請求過程。而後咱們就知道了,原來Http請求到服務器,原來是走這樣一條路。
可是咱們忽略了,這個過程跟咱們程序,跟咱們代碼怎麼銜接起來的。
當Http請求進入 Asp.Net Runtime之後,它的管道由託管模塊(Managed Modules)和處理程序(Handlers)組成,而且由管道來處理這個 Http請求。這個就是所謂的ASP的Http管道模型了。
接下來咱們看下這個管道模型是怎麼流動的。(WebForm)
- 首先進來確定是HTTPRuntime,而後經過HttpApplicationFactory建立HttpApplication。
- HttpApplication會建立該次Http請求的HttpContext(上下文),那這個對象咱們就很熟悉了,裏面就包括HttpRequest、HttpResponse、HttpSessionState等。
- 那接下來請求會經過一系列的Module(能夠理解爲車間,能夠作經過的物品作事情),Module能夠作一些執行某個實際工做前的事情。由於它會Http請求有徹底的控制權。
-
當Http請求完,它會被HttpHandler處理。在這一步,也就是咱們實際作的事情了,他能夠完成咱們.aspx的全部業務。WebForm的每一個頁面都是繼承「Page」,而「Page」繼承了「IHttpHandler」接口
public class Page : TemplateControl, IHttpHandler{ // 代碼省略 }
- HttpHandler處理完後,又會回到Module,這時能夠作一些實際工做後的事情。
咱們不少事件,是否是有分前與後,就是這個道理。進行事件的先後攔截。 -
咱們能夠看下,在只有Module與Handler的狀況,整個Http的流動走向,大概是這樣子
3.4 那講完WebForm的,咱們來說MVC的
在IIS7以前,如IIS6或IIS5,請求處理管道分爲兩個:IIS請求處理管道和ASP.NET管道,若客戶端請求靜態資源則只有IIS管道進行處理,而ASP.NET管道不會處理該請求。從IIS7開始兩個管道合二爲一,稱爲集成管道。其實就是咱們IIS裏會選擇到應用程序池的託管模式。
再次瞭解
-
IIS 6以及IIS 7經典模式
早期的IIS版本中,IIS接收到一個請求時先判斷請求類型,若是是靜態文件直接由IIS處理;若是是一個ASP.NET請求類型,IIS把請求發送給IIS的擴展接口ASP.NET ISAPI DLL。ISAPI至關於ASP.NET應用的容器,這也是咱們以前講的。 -
IIS 7 集成模式
IIS 7和以前的版本區別比較大,IIS7直接把ASP.NET的運行管道流程集成到了IIS上。- 在IIS7中,ASP.NET請求處理管道Pipeline直接覆蓋了IIS自己的管道Pipeline,IIS整個流程按照ASP.ENT管道流程執行,例如BeginRequest、AuthenticateRequest、…、EndRequest。而不是經過插件擴展(ISAPI)形式,不一樣的內容(jpg、html、php等)經過不一樣的插件來執行。
- IIS7的優點體如今哪裏?開發人員能夠實現自定義的HttpModule或者HttpHandler,而後直接集成到IIS上,例如,我能夠自定義一個JpgHttpHandler,而後經過IIS的Handler部署方式,把JpgHttpHandler部署到IIS上,處理全部的請求格式爲*.jpg的請求。
知道這些咱們繼續瞭解MVC的管道模式
廢話很少說,直接上圖
廢話也很少說,直接走下流程(以個人方式理解)
廢話有點多
- 首先進入HttpRuntime,在同樣經過HttpApplicationFactory建立HttpApplication
- HttpApplication會建立該次Http請求的HttpContext(上下文),那這個對象咱們就很熟悉了,裏面就包括HttpRequest、HttpResponse、HttpSessionState等。(這些都跟webform是同樣的)
- HttpApplication繼承IHttpHandler,接着咱們開始走Module
- 而後咱們進入到使用UrlRoutingModule(路由系統)UrlRoutingModule,從Http請求獲取Controller和Action以及路由數據。
- 接着匹配Route規則,獲取Route對象,解析對象。
- 請求IRouteHandler(MVCRouteHandler)
- 執行ProcessRequest方法,而後使用ControllerBulider獲取ControllerFactory
- 接着就調用到Controller,一樣的的方法到達Action,並執行
- 在執行Controller與Action可能會有各類認證,各類特性攔截
- 程序猿創造世界
- 返回ActionResult
- 響應Http
- 客戶端接收響應
接下來咱們具體瞭解下細節
-
HttpApplication與HttpModule
HTTP請求由ASP.NET運行時接管以後,HttpRuntime會利用HttpApplicationFactory建立或從HttpApplication對象池(.NET中相似的機制有線程池和字符串拘留池)中取出一個HttpApplication對象,同時ASP.NET會根據配置文件來初始化註冊的HttpModule,HttpModule在初始化時會訂閱HttpApplication中的事件來實現對HTTP請求的處理。
public class HttpApplication : IComponent, IDisposable, IHttpAsyncHandler, IHttpHandler, IRequestCompletedNotifier, ISyncContext{ public HttpApplication(); public ISite Site { get; set; } public IPrincipal User { get; } ... }
很明顯咱們能夠看到HttpApplication繼承了IHttpHandler,是跟WebForm的Page是同樣的
-
Route
一個HTTP請求會通過至少一個HttpModule的處理。UrlRoutingModule是很是重要的模塊,它是路由系統的核心。路由系統的職責是從請求URL中獲取controller和action的名稱以及其它請求數據。
UrlRoutingModule根據當前請求的URL和RouteTable中已註冊的路由模板進行匹配並返回第一個和當前請求相匹配的路有對象Route,而後根據路有對象獲取路由數據對象RouteData(ASP.NET MVC中,路由數據必須包含controller和action的名稱),再有RouteData獲取IRouteHandler最終有IRouteHandler獲得IHttpHandler -
HttpHandler
一個HTTP請求最終要進入HttpHanler中進行處理,一次HTTP請求只能被一個HttpHandler進行處理。
-
Controller
IHttpHandler在ProcessRequest方法中對當前請求進行處理,在該方法中經過ControllerBuilder獲得IControllerFactory而後經過反射的方式獲取Controller的類型。 -
Action
ASP.NET MVC中ControllerBase是全部Controller的基類,在該類型的Execute方法中經過IActionInvoker的InvokeAction方法來執行對Action的調用。在Action執行前會進行模型綁定和模型認證操做。
-
Filters
在ASP.NET MVC5中有經常使用的過濾器有5個:IAuthenticationFilter、IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter。
在ASP.NET MVC中全部的過濾器最終都會被封裝爲Filter對象,該對象中FilterScope類型的屬性Scope和int類型屬性Order用於決定過濾器執行的前後順序,具體規則以下:
Order和FilterScope的數值越小,過濾器的執行優先級越高;
Order比FilterScope具備更高的優先級,在Order屬性值相同時FilterScope纔會被考慮//數值越小,執行優先級越高 public enum FilterScope { Action= 30, Controller= 20, First= 0, Global= 10, Last= 100 }
-
ActionResult
Action執行完畢以後會返回ActionResult類型對象做爲對這次請求進行處理的結果,對於不是ActionResult類型的返回值,ASP.NET MVC會將其轉換爲ActionResult類型。
總結:那這個就是MVC模式下,一個大概的請求走向,在MVC的模式下,它的管道是啥樣子的。
3.5 看完MVC ,咱們接着來看最牛的Core,誰叫它能夠跨平臺呢【傲嬌】
既然Core是跨平臺的,那麼它不依託IIS,如今的IIS就是個擺設,它有獨立的Core的SDK,Core的RunTime。咱們來一探究竟!
Core是跨平臺的,那他是怎麼實現的呢,其實他本身自己有本身的Kestrel Server,能夠直接對外部提供服務。可是還須要有個反向代理服務器將他保護起來,IIS就是其一,其餘平臺有其餘的方式。
上圖:
知識點:IIS 是經過 HTTP 的方式來調用咱們的 ASP.NET Core 程序。而部署在IIS中時,並不須要咱們手動來啓動 ASP.NET Core 的控制檯程序,這是由於IIS新增了一個 AspNetCoreModule 模塊,它負責 ASP.NET Core 程序的啓動與中止,並能監聽 ASP.NET Core 程序的狀態,在咱們的應用程序意外崩潰時從新啓動。
-
Hostting(宿主)
IIS經過Http調用Core應用程序,因此咱們確定要建立一個Host來啓動Core,WebHost
,上面咱們也講到宿主,咱們經過宿主在生成一系列的Http的上下文,環境等。整個http請求都在宿主內。-
WebHost的建立
public class Program{
public static void Main(string[] args){
CreateWebHostBuilder(args).Build().Run();
}public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }
咱們能夠看到WeHost經過靜態自己來調用建立出來的。「CreateDefaultBuilder」用來作一些簡單的配置
- 註冊 Kestrel 中間件,指定 WebHost 要使用的 Server(HTTP服務器)。
- 設置 Content 根目錄,將當前項目的根目錄做爲 ContentRoot 的目錄。
- 讀取 appsettinggs.json 配置文件,開發環境下的 UserSecrets 以及環境變量和命令行參數。
- 讀取配置文件中的 Logging 節點,對日誌系統進行配置。
- 添加 IISIntegration 中間件。
- 設置開發環境下, ServiceProvider 的 ValidateScopes 爲 true,避免直接在 Configure 方法中獲取 Scope 實例。
接着指定
Startup
最終使用Build
建立WebHost。緊接着使用Run
將程序跑起來。 - WebHost的啓動
- 初始化,構建 RequestDelegate
RequestDelegate 是咱們的應用程序處理請求,輸出響應的整個過程,也就是咱們的 ASP.NET Core 請求管道。- 調用 Startup 中的 ConfigureServices 方法
- 初始化 Http Server
Server 是一個HTTP服務器,負責HTTP的監聽,接收一組 FeatureCollection 類型的原始請求,並將其包裝成 HttpContext 以供咱們的應用程序完成響應的處理。 - 建立 IApplicationBuilder
IApplicationBuilder 用於構建應用程序的請求管道,也就是生成 RequestDelegate - 配置 IApplicationBuilder
- 啓動 Server,監聽請求並響應
- 啓動 HostedService
那這邊是簡單的講了下WebHost,具體的內容能夠看下文章末尾的Core的連接。
- 初始化,構建 RequestDelegate
-
- Middleware(中間件),管道模型的構成
- IApplicationBuilder
首先,IApplicationBuilder 是用來構建請求管道的,而所謂請求管道,本質上就是對 HttpContext 的一系列操做,即經過對 Request 的處理,來生成 Reponse。所以,在 ASP.NET Core 中定義了一個 RequestDelegate 委託,來表示請求管道中的一個步驟,它有以下定義:public delegate Task RequestDelegate(HttpContext context);
而對請求管道的註冊是經過Func<RequestDelegate, RequestDelegate>
類型的委託(也就是中間件)來實現的。它接收一個 RequestDelegate 類型的參數,並返回一個 RequestDelegate 類型,也就是說前一箇中間件的輸出會成爲下一個中間件的輸入,這樣把他們串聯起來,造成了一個完整的管道。
它有一個內部的 Func<RequestDelegate, RequestDelegate> 類型的集合(用來保存咱們註冊的中間件)和三個核心方法:
1. UseUse
是咱們很是熟悉的註冊中間件的方法,其實現很是簡單,就是將註冊的中間件保存到其內部屬性 _components 中。
2. Build
在Hosting
的啓動中,即是經過該 Build 方法建立一個 RequestDelegate 類型的委託,Http Server 經過該委託來完成整個請求的響應
3. Run
在咱們註冊的中間件中,是經過 Next 委託 來串連起來的,若是在某一箇中間件中沒有調用 Next 委託,則該中間件將作爲管道的終點,所以,咱們在最後一箇中間件不該該再調用 Next 委託,而 Run 擴展方法,一般用來註冊最後一箇中間件
4. NewNew
方法根據自身來「克隆」了一個新的 ApplicationBuilder 對象,而新的 ApplicationBuilder 能夠訪問到建立它的對象的Properties
屬性,可是對自身 Properties 屬性的修改,卻不到影響到它的建立者,這是經過 CopyOnWriteDictionary 來實現的
因此 Core的管道其實就是Middleware來作的,每一個都有前置,後置的處理步驟,中間能夠調用其餘Middleware。也能夠並行走向。
- IApplicationBuilder
ASP.NET Core 請求管道的構建過程,以及一些幫助咱們更加方便的來配置請求管道的擴展方法。在 ASP.NET Core 中,至少要有一箇中間件來響應請求,而咱們的應用程序實際上只是中間件的集合,MVC 也只是其中的一箇中間件而已。簡單來講,中間件就是一個處理http請求和響應的組件,多箇中間件構成了請求處理管道,每一箇中間件均可以選擇處理結束,仍是繼續傳遞給管道中的下一個中間件,以此串聯造成請求管道。一般,咱們註冊的每一箇中間件,每次請求和響應均會被調用,但也可使用 Map
, MapWhen
,UseWhen
等擴展方法對中間件進行過濾。
3.6總結
那咱們分析了以往微軟的3種形式底下的Web,分別是WebForm,MVC,Core,其中WebForm與MVC雷士,依託於IIS,不一樣的就是ISAPI
的不一樣。前者的ISAPI以插件形式存在IIS,然後者將其整合成一套。Core跨平臺,IIS只是他的一個反向代理服務器,HTTP請求帶Core的程序,運行Core。
可是我以爲他們內容是基本不變的,HTTP請求,進來在彼此的AppDomain
中建立HttpContext
,而後就開始走Pipeline
走流程,WebFrom走Page
的IHTTPHandler
與其相關的IModule,而MVC是Global.asax
繼承HttpApplication
,HttpApplication
又繼承IHTTPHandler
,接下來WebFrom與MVC就各自走各自的Pipeline
。Core卻不是這樣,它的Pipeline
採用了Middleware
(中間件)的形式。俄羅斯套娃,一步一步走Pipeline
。最終都是響應客戶端,一個HTTP請求,從開始到結束。