一個由正則表達式引起的血案 vs2017使用rdlc實現批量打印 vs2017使用rdlc [asp.net core 源碼分析] 01 - Session SignalR sql for xml p

1. 血案由來

近期我在爲Lazada賣家中心作一個自助註冊的項目,其中的shop name校驗規則較爲複雜,要求:
1. 英文字母大小寫
2. 數字
3. 越南文
4. 一些特殊字符,如「&」,「-」,「_」等
看到這個要求的時候,天然而然地想到了正則表達式。因而就有了下面的表達式(寫的比較齪):javascript

  1. ^([A-Za-z0-9._()&'\- ]|[aAàÀảẢãÃáÁạẠăĂằẰẳẲẵẴắẮặẶâÂầẦẩẨẫẪấẤậẬbBcCdDđĐeEèÈẻẺẽẼéÉẹẸêÊềỀểỂễỄếẾệỆfFgGhHiIìÌỉỈĩĨíÍịỊjJkKlLmMnNoOòÒỏỎõÕóÓọỌôÔồỒổỔỗỖốỐộỘơƠờỜởỞỡỠớỚợỢpPqQrRsStTuUùÙủỦũŨúÚụỤưƯừỪửỬữỮứỨựỰvVwWxXyYỳỲỷỶỹỸýÝỵỴzZ])+$ 

在測試環境,這個表達式從功能上符合業務方的要求,就被髮布到了馬來西亞的線上環境。結果上線以後,發現線上機器時有發生CPU飆到100%的狀況,致使整個站點響應異常緩慢。經過dump線程trace,才發現線程所有卡在了這個正則表達式的校驗上:html

一開始難以置信,一個正則表達式的匹配過程怎麼可能引起CPU飈高呢?抱着懷疑的態度去查了資料才發現小小的正則表達式裏面居然大有文章,平時寫起來都是淺嘗輒止,只要可以知足功能需求,就認爲達到目的了,徹底忽略了它可能帶來的性能隱患。java

引起此次血案的就是所謂的正則「回溯陷阱(Catastrophic Backtracking)」。下面詳細介紹下這個問題,以免重蹈覆轍。jquery

 

2. 正則表達式引擎

提及回溯陷阱,要先從正則表達式的引擎提及。正則引擎主要能夠分爲基本不一樣的兩大類:一種是DFA(肯定型有窮自動機),另外一種是NFA(不肯定型有窮自動機)。簡單來說,NFA 對應的是正則表達式主導的匹配,而 DFA 對應的是文本主導的匹配。git

DFA從匹配文本入手,從左到右,每一個字符不會匹配兩次,它的時間複雜度是多項式的,因此一般狀況下,它的速度更快,但支持的特性不多,不支持捕獲組、各類引用等等;而NFA則是從正則表達式入手,不斷讀入字符,嘗試是否匹配當前正則,不匹配則吐出字符從新嘗試,一般它的速度比較慢,最優時間複雜度爲多項式的,最差狀況爲指數級的。但NFA支持更多的特性,於是絕大多數編程場景下(包括java,js),咱們面對的是NFA。如下面的表達式和文本爲例,程序員

  1. text after tonight regex to(nite|nighta|night)’ 

在NFA匹配時候,是根據正則表達式來匹配文本的,從t開始匹配a,失敗,繼續,直到文本里面的第一個t,接着比較o和e,失敗,正則回退到 t,繼續,直到文本里面的第二個t,而後 o和文本里面的o也匹配,繼續,正則表達式後面有三個可選條件,依次匹配,第一個失敗,接着2、三,直到匹配。github

而在DFA匹配時候,採用的是用文原本匹配正則表達式的方式,從a開始匹配t,直到第一個t跟正則的t匹配,但e跟o匹配失敗,繼續,直到文本里面的第二個 t 匹配正則的t,接着o與o匹配,n的時候發現正則裏面有三個可選匹配,開始並行匹配,直到文本中的g使得第一個可選條件不匹配,繼續,直到最後匹配。web

能夠看到,DFA匹配過程當中文本中的字符每個只比較了一次,沒有吐出的操做,應該是快於NFA的。另外,無論正則表達式怎麼寫,對於DFA而言,文本的匹配過程是一致的,都是對文本的字符依次從左到右進行匹配,因此,DFA在匹配過程當中是跟正則表達式無關的,而 NFA 對於不一樣但效果相同的正則表達式,匹配過程是徹底不一樣的。正則表達式


3. 回溯

說完了引擎,咱們再來看看到底什麼是回溯。對於下面這個表達式,相信你們很清楚它的意圖,算法

  1. ab{1,3}

也就是說中間的b須要匹配1~3次。那麼對於文本「abbbc」,按照第1部分NFA引擎的匹配規則,實際上是沒有發生回溯的,在表達式中的a匹配完成以後,b剛好和文本中的3個b完整匹配,以後是c發生匹配,一鼓作氣。若是咱們把文本換成「abc」呢?無非就是少了一個字母b,卻發生了所謂的回溯。匹配過程以下圖所示(橙色爲匹配,黃色爲不匹配),

1~2步應該都好理解,可是爲何在第3步開始,雖然已經文本中已經有一個b匹配了b{1,3},後面還會拉着字母c跟b{1,3}作比較呢?這個就是咱們下面將要提到的正則的貪婪特性,也就是說b{1,3}會竭盡所能的匹配最多的字符。在這個地方咱們先知道它一直要匹配到撞上南牆爲止。 在這種狀況下,第3步發生不匹配以後,整個匹配流程並無走完,而是像棧同樣,將字符c吐出來,而後去用正則表達式中的c去和文本中的c進行匹配。這樣就發生了一次回溯。

4. 貪婪、懶惰與獨佔

咱們再來看一下究竟什麼是貪婪模式。

下面的幾個特殊字符相信你們都知道它們的用法:

i. ?: 告訴引擎匹配前導字符0次或一次。事實上是表示前導字符是可選的。
ii. +: 告訴引擎匹配前導字符1次或屢次。
iii. *: 告訴引擎匹配前導字符0次或屢次。
iv. {min, max}: 告訴引擎匹配前導字符min次到max次。min和max都是非負整數。若是有逗號而max被省略了,則表示max沒有限制;若是逗號和max都被省略了,則表示重複min次。

默認狀況下,這個幾個特殊字符都是貪婪的,也就是說,它會根據前導字符去匹配儘量多的內容。這也就解釋了爲何在第3部分的例子中,第3步之後的事情會發生了。

在以上字符後加上一個問號(?)則能夠開啓懶惰模式,在該模式下,正則引擎儘量少的重複匹配字符,匹配成功以後它會繼續匹配剩餘的字符串。在上例中,若是將正則換爲

  1. ab{1,3}?

則匹配過程變成了下面這樣(橙色爲匹配,黃色爲不匹配),

因而可知,在非貪婪模式下,第2步正則中的b{1,3}?與文本b匹配以後,接着去用c與文本中的c進行匹配,而未發生回溯。

若是在以上四種表達式後加上一個加號(+),則會開啓獨佔模式。同貪婪模式同樣,獨佔模式同樣會匹配最長。不過在獨佔模式下,正則表達式儘量長地去匹配字符串,一旦匹配不成功就會結束匹配而不會回溯。咱們如下面的表達式爲例,

  1. ab{1,3}+bc 

若是咱們用文本"abbc"去匹配上面的表達式,匹配的過程以下圖所示(橙色爲匹配,黃色爲不匹配),

能夠發現,在第2和第3步,b{1,3}+會將文本中的2個字母b都匹配上,結果文本中只剩下一個字母c。那麼在第4步時,正則中的b和文本中的c進行匹配,當沒法匹配時,並不進行回溯,這時候整個文本就沒法和正則表達式發生匹配。若是將正則表達式中的加號(+)去掉,那麼這個文本總體就是匹配的了。

把以上三種模式的表達式列出以下,

 

貪婪

懶惰

獨佔

X?

X??

X?+

X*

X*?

X*+

X+

X+?

X++

X{n}

X{n}?

X{n}+

X{n,}

X{n,}?

X{n,}+

X{n,m}

X{n,m}?

X{n,m}+


5. 總結

如今再回過頭看看文章開頭的那個很長的正則表達式,其實簡化以後,就是一個形如

  1. ^[容許字符集]+ 

的表達式。該字符集大小約爲250,而+號表示至少出現一次。按照上面說到的NFA引擎貪婪模式,在用戶輸入一個過長字符串進行匹配時,一旦發生回溯,計算量將是巨大的。後來採用了獨佔模式,CPU 100%的問題也獲得瞭解決。

所以,在本身寫正則表達式的時候,必定不能大意,在實現功能的狀況下,還要仔細考慮是否會帶來性能隱患。

關於正則表達式,你有哪些想要分享的特殊技能?歡迎在下面留言,一塊兒交流探討。

 

 

vs2017使用rdlc實現批量打印

 

接着上一篇:上一篇寫了安裝,這篇直接搞定批量打印,A4紙橫版豎版頁面設計,正式開始。(個人表達不怎麼好,我儘可能發圖片都是程序員一點就通)

1、界面展現

忽略界面設計醜

查看預覽界面,由於有數據就不截全屏了,盒號是我本身加的,咱們本身的業務邏輯。

3、核心代碼,批量打印(參考代碼連接,放到文章結尾處)

複製代碼
  1   public class BillPrint : IDisposable
  2     {
  3         /// <summary>
  4         /// 當前打印頁號
  5         /// </summary>
  6         static int m_currentPageIndex;
  7 
  8         /// <summary>
  9         /// RDCL轉換stream一頁對應一個stream
 10         /// </summary>
 11         static List<Stream> m_streams;
 12 
 13         /// <summary>
 14         /// 把report輸出成stream
 15         /// </summary>
 16         /// <param name="report">傳入須要Export的report</param>
 17         private void Export(LocalReport report)
 18         {
 19             string deviceInfo =
 20               "<DeviceInfo>" +
 21               "  <OutputFormat>EMF</OutputFormat>" +
 22               //"  <PageWidth>2in</PageWidth>" +
 23               //"  <PageHeight>20in</PageHeight>" +
 24               //"  <MarginTop>0in</MarginTop>" +
 25               //"  <MarginLeft>0in</MarginLeft>" +
 26               //"  <MarginRight>0in</MarginRight>" +
 27               //"  <MarginBottom>0in</MarginBottom>" +
 28               "</DeviceInfo>";
 29             m_streams = new List<Stream>();
 30             report.Render("Image", deviceInfo, CreateStream, out Warning[] warnings);
 31             foreach (Stream stream in m_streams)
 32                 stream.Position = 0;
 33         }
 34 
 35         /// <summary>
 36         /// 建立具備指定的名稱和格式的流。
 37         /// </summary>
 38         private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
 39         {
 40             Stream stream = new FileStream(name + "." + fileNameExtension,
 41               FileMode.Create);
 42             m_streams.Add(stream);
 43             return stream;
 44         }
 45 
 46         /// <summary>
 47         /// 打印輸出
 48         /// </summary>
 49         private void PrintPage(object sender, PrintPageEventArgs ev)
 50         {
 51             Metafile pageImage =
 52               new Metafile(m_streams[m_currentPageIndex]);
 53             ev.Graphics.DrawImage(pageImage, ev.PageBounds);
 54             m_currentPageIndex++;
 55             ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
 56         }
 57         /// <summary>
 58         /// 設置橫版打印
 59         /// </summary>
 60         /// <param name="sender"></param>
 61         /// <param name="e"></param>
 62         void Document_QueryPageSettings(object sender, QueryPageSettingsEventArgs e)
 63         {
 64             e.PageSettings.Landscape = false;
 65             int index = -1;
 66             for (int i = 0; i < e.PageSettings.PrinterSettings.PaperSizes.Count; i++)
 67             {
 68                 if (e.PageSettings.PrinterSettings.PaperSizes[i].PaperName == "A4")
 69                 {
 70                     index = i;
 71                     break;
 72                 }
 73             }
 74             if (index != -1)
 75             {
 76                 e.PageSettings.PaperSize = e.PageSettings.PrinterSettings.PaperSizes[index];
 77             }
 78         }
 79 
 80 
 81         /// <summary>
 82         /// 打印預處理
 83         /// </summary>
 84         private void Print(string printerName = null)
 85         {
 86             PrintDocument printDoc = new PrintDocument();
 87             if (string.IsNullOrEmpty(printerName))
 88             {
 89                 printerName = printDoc.PrinterSettings.PrinterName;
 90             }
 91             if (m_streams == null || m_streams.Count == 0)
 92                 return;
 93             printDoc.PrinterSettings.PrinterName = printerName;
 94             if (!printDoc.PrinterSettings.IsValid)
 95             {
 96                 string msg = String.Format("Can't find printer \"{0}\".", printerName);
 97                 throw new Exception(msg);
 98             }
 99             printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
100 
101             //設置橫版打印
102             printDoc.QueryPageSettings += new QueryPageSettingsEventHandler(Document_QueryPageSettings);
103 
104             StandardPrintController spc = new StandardPrintController();
105             printDoc.PrintController = spc;
106             printDoc.Print();
107         }
108 
109         /// <summary>
110         /// 對外接口,啓動打印
111         /// </summary>
112         /// <param name="report"></param>
113         /// <param name="printerName">默認打印機</param>
114         public static void Run(LocalReport report, string printerName = null)
115         {
116             m_currentPageIndex = 0;
117             BillPrint billPrint = new BillPrint();
118             billPrint.Export(report);
119             billPrint.Print();
120             billPrint.Dispose();
121         }
122 
123 
124         /// <summary>
125         /// 獲取打印機狀態
126         /// </summary>
127         /// <param name="printerName">打印機名稱</param>
128         /// <param name="status">輸出打印機狀態</param>
129         private static void GetPrinterStatus2(string printerName, ref uint status)
130         {
131             try
132             {
133                 string lcPrinterName = printerName;
134                 IntPtr liHandle = IntPtr.Zero;
135                 if (!Win32.OpenPrinter(lcPrinterName, out liHandle, IntPtr.Zero))
136                 {
137                     Console.WriteLine("print  is close");
138                     return;
139                 }
140                 UInt32 level = 2;
141                 IntPtr buffer = IntPtr.Zero;
142                 Win32.GetPrinter(liHandle, level, buffer, 0, out uint sizeNeeded);
143                 buffer = Marshal.AllocHGlobal((int)sizeNeeded);
144                 if (!Win32.GetPrinter(liHandle, level, buffer, sizeNeeded, out sizeNeeded))
145                 {
146                     Console.WriteLine(Environment.NewLine + "Fail GetPrinter:" + Marshal.GetLastWin32Error());
147                     return;
148                 }
149 
150                 Win32.PRINTER_INFO_2 info = (Win32.PRINTER_INFO_2)Marshal.PtrToStructure(buffer, typeof(Win32.PRINTER_INFO_2));
151                 status = info.Status;
152                 Marshal.FreeHGlobal(buffer);
153                 Win32.ClosePrinter(liHandle);
154             }
155             catch (Exception ex)
156             {
157                 throw ex;
158             }
159         }
160 
161         /// <summary>
162         /// 對外接口,調去打印機信息
163         /// </summary>
164         /// <param name="printerName">打印機名稱</param>
165         /// <returns>返回打印機當前狀態</returns>
166         public static string GetPrinterStatus(string printerName)
167         {
168             uint intValue = 0;
169             PrintDocument pd = new PrintDocument();
170             printerName = printerName == "" ? pd.PrinterSettings.PrinterName : printerName;
171             GetPrinterStatus2(printerName, ref intValue);
172             string strRet = string.Empty;
173             switch (intValue)
174             {
175                 case 0:
176                     strRet = "準備就緒(Ready)";
177                     break;
178                 case 4194432:
179                     strRet = "被打開(Lid Open)";
180                     break;
181                 case 144:
182                     strRet = "打印紙用完(Out of Paper)";
183                     break;
184                 case 4194448:
185                     strRet = "被打開而且打印紙用完(Out of Paper && Lid Open)";
186                     break;
187                 case 1024:
188                     strRet = "打印中(Printing)";
189                     break;
190                 case 32768:
191                     strRet = "初始化(Initializing)";
192                     break;
193                 case 160:
194                     strRet = "手工送紙(Manual Feed in Progress)";
195                     break;
196                 case 4096:
197                     strRet = "脫機(Offline)";
198                     break;
199                 default:
200                     strRet = "未知狀態(unknown state)";
201                     break;
202             }
203             return strRet;
204         }
205 
206 
207         public void Dispose()
208         {
209             if (m_streams != null)
210             {
211                 foreach (Stream stream in m_streams)
212                     stream.Close();
213                 m_streams = null;
214             }
215         }
216     }
217 
218     public class Win32
219     {
220         [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
221         public static extern bool OpenPrinter(string printer, out IntPtr handle, IntPtr printerDefaults);
222         [DllImport("winspool.drv")]
223         public static extern bool ClosePrinter(IntPtr handle);
224         [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
225         public static extern bool GetPrinter(IntPtr handle, UInt32 level, IntPtr buffer, UInt32 size, out UInt32 sizeNeeded);
226         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
227         public struct PRINTER_INFO_2
228         {
229             public string pServerName;
230             public string pPrinterName;
231             public string pShareName;
232             public string pPortName;
233             public string pDriverName;
234             public string pComment;
235             public string pLocation;
236             public IntPtr pDevMode;
237             public string pSepFile;
238             public string pPrintProcessor;
239             public string pDatatype;
240             public string pParameters;
241             public IntPtr pSecurityDescriptor;
242             public UInt32 Attributes;
243             public UInt32 Priority;
244             public UInt32 DefaultPriority;
245             public UInt32 StartTime;
246             public UInt32 UntilTime;
247             public UInt32 Status;
248             public UInt32 cJobs;
249             public UInt32 AveragePPM;
250         }
251     }
複製代碼

代碼使用

複製代碼
 1   using (ReportViewer rvDoc = new ReportViewer())
 2                             {
 3                                 rvDoc.LocalReport.DataSources.Add(new ReportDataSource("DataSet1", 你的數據));
 4                                 //注意本身的路徑,這塊我寫到配置文件,來區分測試跟線上路徑。
 5                                 if (PublicProperty.RdlcPath == "Debug")
 6                                 {
 7                                     rvDoc.LocalReport.ReportPath = @"..\..\ReportForm\GTTKWenShuArchives.rdlc";
 8                                 }
 9                                 else
10                                 {
11                                     rvDoc.LocalReport.ReportPath = Application.StartupPath + "\\ReportForm\\GTTKWenShuArchives.rdlc";
12                                 }
13                                 //開始打印,第二個參數是選擇打印機名稱
14                                 BillPrint.Run(rvDoc.LocalReport, (string)printerList.Invoke(new obj_delegate(() => { return printerList.SelectedItem.ToString(); })));
15                             }
16                             }
複製代碼

4、設計報表一些注意事項(能夠用差之毫釐失之千里來形容)

  1. A4豎版打印,標頭設計寬,長只能小於等於寬,要是大於就會出現空白頁狀況。

     

  2. A4橫版打印,標頭設計寬,長度跟豎版同樣,注意這個數字是我一點點試出來的,多一點就會出現表的列顯示不全,會跑到第二頁裏面,你們也能夠本身試試。

     

  3. 要想每一頁都顯示標題,只能把標題加入到頁眉之中,注意頁眉的底部必定要跟表重合不然到第二頁跟上邊距會跟第一頁不同,具體什麼樣本身試一下就知道了,

     

  4. 表要是想加實線,注意設計的時候,這個你們一試便知。

     

  5. 要想每一頁都顯示錶的標題部分能夠這麼設計
  6. 剩下的內容的字體啊間距啊,就根據本身需求本身調吧,注意設計的時候儘可能表要與兩邊重合,標題要與頂部重合,由於他默認是上下左右間距都是2CM,你要是有距離你打印出來就很差看了,這個本身試試就知道了。

5、結尾

把一些我參考的連接放出來,你們能夠自行參考。

https://blog.csdn.net/nuptsv_ice/article/details/41821611

有個批量打印代碼連接找不到了,要是找到會補上去的。

 

vs2017使用rdlc

 

寫在前面:由於公司要求作個批量打印工具,之前用Delphi+FastReport開發的,如今由於公司就剩下一個Delphi開發工程師了,還外出,因此這是就落在我身上。由於這個打印工具不須要使用人員設計,只要個模板打印就行, 我這用的工具是vs2017+winfrom+rdlc,好像FastReport收費了。

1、vs2017配置rdlc

由於vs2017默認沒有裝報表的須要自行安裝,安裝方法工具>擴展和更新>聯機>搜索rdlc默認第一個安裝便可,可能有點慢,個人安裝的很長時間,你也能夠本身去單獨下載。安裝完成後重啓vs2017就有了。

2、生成項目

 默認有個嚮導,可根據本身需求添加,也能夠取消,後續本身添加。

項目結構

修改屬性

3、設計報表,生成數據

下面設計一個打印界面,上面嚮導的時候咱們把添加數據集跳過了,如今咱們自行添加數據,

這裏咱們自定義列,也能夠從數據庫獲取

而後添加數據集

到咱們winfrom界面添加報表

後臺數據綁定

點擊啓動便可看到如下界面,對文檔的字體大小均可以經過設計頁面進行設計

3、結尾

基本使用到此結束,剩下的就自行擴展了,至於批量打印功能,我這尚未申請到打印機,等後續打印機到手,進行測試以後補上。

 

[asp.net core 源碼分析] 01 - Session

 

 一、Session文檔介紹

  1. 毋庸置疑學習.Net core最好的方法之一就是學習微軟.Net core的官方文檔;https://docs.microsoft.com/zh-cn/aspnet/core
  2. .Net core Session的官方文檔 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/app-state
  3. .Net core Session Github源碼 https://github.com/aspnet/Session

二、Session簡單應用

2.一、在Startup類的ConfigureServices方法中添加

services.AddDistributedMemoryCache();
services.AddSession();

由於Session的服務端存儲須要緩存,因此須要引入.Net core的緩存DistributedMemoryCache;

2.二、在Startup類的Configure方法中添加

app.UseSession();

2.三、使用(存儲和獲取)

// 存儲
HttpContext.Session.Set("LoginId", System.Text.Encoding.Default.GetBytes("666"));

// 獲取
HttpContext.Session.TryGetValue("LoginId", out byte[] byteLoginId);
var loginId = System.Text.Encoding.Default.GetString(byteLoginId); // LoginId="666";

 三、源碼分析圖

 

 

 

 四、源碼分析

4.一、程序加載

4.1.一、在ConfigureServices中添加分佈式緩存,services.AddDistributedMemoryCache();

微軟官方建議使用AddDistributedMemoryCache,固然也可使用AddMemoryCache、AddDistributedRedisCache、AddDistributedSqlServerCache或者自定義緩存也是能夠的;

若是是分佈式系統或者SSO單點登陸,建議使用分佈式的緩存,不要使用AddMemoryCache;

緩存的官方文檔 https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/memory

4.1.二、在ConfigureServices中添加AddSession;

複製代碼
 1 public static IServiceCollection AddSession(this IServiceCollection services)
 2 {
 3     if (services == null)
 4     {
 5         throw new ArgumentNullException(nameof(services));
 6     }
 7 
 8     services.AddTransient<ISessionStore, DistributedSessionStore>();
 9     services.AddDataProtection();
10     return services;
11 }
12  
13 public static IServiceCollection AddSession(this IServiceCollection services, Action<SessionOptions> configure)
14 {
15     if (services == null)
16     {
17         throw new ArgumentNullException(nameof(services));
18     }
19 
20     if (configure == null)
21     {
22         throw new ArgumentNullException(nameof(configure));
23     }
24 
25     services.Configure(configure);
26     services.AddSession();
27 
28     return services;
29 }
複製代碼

AddSession爲IServiceCollection的擴展方法,有1個重載(傳入Session的設置,使用services.Configure(configure),加載設置);

services.AddDataProtection()注入數據加密解密DataProtection(),在加密解密SessionKey時使用;

services.AddTransient<ISessionStore, DistributedSessionStore>();注入DistributedSessionStore,其中的Create 方法用作建立Session,調用Create方法時執行new DistributedSession();  DistributedSession類中包含了對IDictionary<EncodedKey, byte[]>的增刪改查;

 4.1.三、在Configure中UseSession

複製代碼
 1 public static IApplicationBuilder UseSession(this IApplicationBuilder app)
 2 {
 3     if (app == null)
 4     {
 5         throw new ArgumentNullException(nameof(app));
 6     }
 7 
 8     return app.UseMiddleware<SessionMiddleware>();
 9 }
10 
11 
12 public static IApplicationBuilder UseSession(this IApplicationBuilder app, SessionOptions options)
13 {
14     if (app == null)
15     {
16         throw new ArgumentNullException(nameof(app));
17     }
18     if (options == null)
19     {
20         throw new ArgumentNullException(nameof(options));
21     }
22 
23     return app.UseMiddleware<SessionMiddleware>(Options.Create(options));
24 }
複製代碼

UseSession爲IApplicationBuilder的擴展方法,也有1個重載,一樣也是加載Session的設置,使用Options.Create(options)結合中間件加載設置;

關於中間件能夠參考文檔 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware

SessionMiddleware.cs爲Session的中間件;其中包含Session的核心代碼,操做MVC以前和以後的代碼都在中間件中;

4.二、SessionMiddleware.cs類解析

 在SessionMiddleware中一個異步方法Invoke;主要邏輯中包含了註釋,應該很好理解;

複製代碼
 1   /// <summary>
 2         /// Invokes the logic of the middleware.
 3         /// </summary>
 4         /// <param name="context">The <see cref="HttpContext"/>.</param>
 5         /// <returns>A <see cref="Task"/> that completes when the middleware has completed processing.</returns>
 6         public async Task Invoke(HttpContext context)
 7         {
 8             var isNewSessionKey = false;
 9             Func<bool> tryEstablishSession = ReturnTrue;
10             var cookieValue = context.Request.Cookies[_options.Cookie.Name];
11 
12             // 解密cookieValue
13             var sessionKey = CookieProtection.Unprotect(_dataProtector, cookieValue, _logger);
14             if (string.IsNullOrWhiteSpace(sessionKey) || sessionKey.Length != SessionKeyLength)
15             {
16 
17                 // 生成36位隨機數
18                 var guidBytes = new byte[16];
19                 CryptoRandom.GetBytes(guidBytes);
20                 sessionKey = new Guid(guidBytes).ToString();
21 
22                 // 加密cookieValue
23                 cookieValue = CookieProtection.Protect(_dataProtector, sessionKey);
24 
25                 // 設置Cookie
26                 var establisher = new SessionEstablisher(context, cookieValue, _options);
27                 tryEstablishSession = establisher.TryEstablishSession;
28                 isNewSessionKey = true;
29             }
30 
31             var feature = new SessionFeature();
32             // 建立Sessin放入 HttpContext Features
33             feature.Session = _sessionStore.Create(sessionKey, _options.IdleTimeout, _options.IOTimeout, tryEstablishSession, isNewSessionKey);
34             context.Features.Set<ISessionFeature>(feature);
35 
36             try
37             {
38                 // 執行邏輯(MVC)之間
39                 await _next(context);
40                 // 執行邏輯(MVC)以後
41             }
42             finally
43             {
44                 // 設置HttpContext Features爲空
45                 context.Features.Set<ISessionFeature>(null);
46 
47                 if (feature.Session != null)
48                 {
49                     try
50                     {
51                         // Commit Session,把 IDictionary<EncodedKey, byte[]>中的值放入緩存
52                         await feature.Session.CommitAsync(context.RequestAborted);
53                     }
54                     catch (OperationCanceledException)
55                     {
56                         _logger.SessionCommitCanceled();
57                     }
58                     catch (Exception ex)
59                     {
60                         _logger.ErrorClosingTheSession(ex);
61                     }
62                 }
63             }
64         }
複製代碼

 4.三、DistributedSession.cs 類解析

在SessionMiddleware Invoke方法中,能夠看到建立Session最終執行的是new DistributedSession();

此類就不作過多的介紹了,主要就是對IDictionary<EncodedKey, byte[]>增刪改查,序列化值、從緩存中Load數據和把數據放入緩存中;

代碼過多就不放置博客上,可移至github :https://github.com/aspnet/Session/blob/master/src/Microsoft.AspNetCore.Session/DistributedSession.cs

五、總結

一、在asp.net core中Session的代碼仍是比較簡單的,運用操做也比較簡單;

二、能夠清楚的理解asp.net core中Session的原理;

三、能夠學習其餘生產隨機數的方法;

四、能夠學習在中間件中怎麼運用設置(Options.Create(options)、services.Configure(configure));

五、知道了中間件的簡單運用;

六、學寫了Httpcontext Features 的簡單運用,關於 HttpContext能夠直接使用Session(HttpContext.Session)在講asp.net core http時會詳細介紹;

七、簡單知道了對於緩存的獲取和增長;

八、下一篇將分析 .net core configuration,敬請關注;

九、記得推薦評論,或者能夠留言但願分析哪部分asp.net core的源碼

 

SignalR

 

  從上面的介紹能夠看出,SignalR既然是爲實時而生的,這樣就決定了其使用場所。具體適用情景有以下幾點:

 

 

Asp.net SignalR是微軟爲實現實時通訊的一個類庫。通常狀況下,signalR會使用JavaScript的長輪詢(long polling)的方式來實現客戶端和服務器通訊,隨着Html5中WebSockets出現,SignalR也支持WebSockets通訊。另外SignalR開發的程序不只僅限制於宿主在IIS中,也能夠宿主在任何應用程序,包括控制檯,客戶端程序和Windows服務等,另外還支持Mono,這意味着它能夠實現跨平臺部署在Linux環境下。

  signalR內部有兩類對象:

  1. Http持久鏈接(Persisten Connection)對象:用來解決長時間鏈接的功能。還能夠由客戶端主動向服務器要求數據,而服務器端不須要實現太多細節,只須要處理PersistentConnection 內所提供的五個事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 便可。
  2. Hub(集線器)對象:用來解決實時(realtime)信息交換的功能,服務端能夠利用URL來註冊一個或多個Hub,只要鏈接到這個Hub,就能與全部的客戶端共享發送到服務器上的信息,同時服務端能夠調用客戶端的腳本。SignalR將整個信息的交換封裝起來,客戶端和服務器都是使用JSON來溝通的,在服務端聲明的全部Hub信息,都會生成JavaScript輸出到客戶端,.NET則依賴Proxy來生成代理對象,而Proxy的內部則是將JSON轉換成對象。

 

SignalR將整個信息的交換封裝起來,客戶端和服務器都是使用JSON來溝通的,在服務端聲明的全部Hub信息,都會生成JavaScript輸出到客戶端,.NET則依賴Proxy來生成代理對象,而Proxy的內部則是將JSON轉換成對象。


SignalR的服務端提供了兩種實現方式,分別是PersistentConnection和Hub,這兩種方式的側重點不一樣:

  PersistentConnection Hub/生成Proxy模式 Hub/非生成Proxy模式
服務端配置

app.Map("/messageConnection", map => 
           { 
               map.RunSignalR<MessageConnection>(); 
           });

app.Map("/messageHub", map => 
           { 
               map.RunSignalR(new Microsoft.AspNet.SignalR.HubConfiguration { EnableJavaScriptProxies = true }); 
           });

app.Map("/messageHub", map => 
            { 
                map.RunSignalR(new Microsoft.AspNet.SignalR.HubConfiguration { EnableJavaScriptProxies = true }); 
            });

引入js文件 jquery-1.6.4.min.js 
jquery.signalR-2.2.0.min.js
jquery-1.6.4.min.js 
jquery.signalR-2.2.0.min.js 
/messageHub/js 
上述js文件是動態生成,其中messageHub的爲服務端定義的路徑
jquery-1.6.4.min.js 
jquery.signalR-2.2.0.min.js
建立鏈接 var connection = $.connection("/message"); var connection = $.connection; var connection = $.hubConnection();
開啓鏈接

connection.start() 
                .done(function () { 
                    connected = true; 
                }) 
                .fail(function () { 
                    alert("鏈接失敗"); 
                });

connection.hub.start() 
                .done(function () { 
                    connected = true; 
                }) 
                .fail(function () { 
                    alert("鏈接失敗"); 
                });

connection.start() 
                .done(function () { 
                    connected = true; 
                }) 
                .fail(function () { 
                    alert("鏈接失敗"); 
                });

代理對象 var proxy = connection.MessageService; 
MessageService是Hub的名稱
var proxy = connection.createHubProxy("MessageService"); 
MessageService是Hub的名稱
定義客戶端方法

proxy.client.hello = function (message) { 
                      console.log(message);   

}

proxy.on("hello", function (message) {        

                  console.log(message);

});

接收消息

connection.received(function (message) { 
                alert(message); 
            });



經過服務器調用客戶端方法實現


經過服務器調用客戶端方法實現
發送消息 connection.send(message); 經過調用服務端方法實現 
proxy.server.hello(message);
經過調用服務端方法實現 
proxy.invoke("hello", message);
設置QueryString 在建立connection時指定 
var connection = $.connection("/messageConnection", { username: "qs" + username });

connection.hub.qs = { username: "qs" + username };

connection.qs = { username: "qs" + username };
設置Cookie document.cookie = "username=" + username; document.cookie = "username=" + username; document.cookie = "username=" + username;
設置State proxy.state.ClientType = "HubAutoProxy"; proxy.state.ClientType = "HubNonAutoProxy";

示例代碼下載

 

 

docs.microsoft t


容易碰到的問題:

 

1.預約義的類型「Microsoft.CSharp.RuntimeBinder.Binder」未定義或未導入:https://blog.csdn.net/rztyfx/article/details/61432763

2.Owin:

Install-Package microsoft.owin.cors 

Update-Package Owin -Reinstall

 

3.關於SignalR鏈接數量問題的記錄:https://blog.csdn.net/Andrewniu/article/details/80243120

 

sql for xml path用法

 

FOR XML PATH 有的人可能知道有的人可能不知道,其實它就是將查詢結果集以XML形式展示,有了它咱們能夠簡化咱們的查詢語句實現一些之前可能須要藉助函數活存儲過程來完成的工做。那麼以一個實例爲主.

        一.FOR XML PATH 簡單介紹

 

             那麼仍是首先來介紹一下FOR XML PATH ,假設如今有一張興趣愛好表(hobby)用來存放興趣愛好,表結構以下:

 

       接下來咱們來看應用FOR XML PATH的查詢結果語句以下:

 

SELECT * FROM @hobby FOR XML PATH

 

       結果:

 

複製代碼
複製代碼
複製代碼
<row>
  <hobbyID>1</hobbyID>
  <hName>登山</hName>
</row>
<row>
  <hobbyID>2</hobbyID>
  <hName>游泳</hName>
</row>
<row>
  <hobbyID>3</hobbyID>
  <hName>美食</hName>
</row>
複製代碼
複製代碼
複製代碼

 

      因而可知FOR XML PATH 能夠將查詢結果根據行輸出成XML各式!

 

      那麼,如何改變XML行節點的名稱呢?代碼以下:     

 

SELECT * FROM @hobby FOR XML PATH('MyHobby')

 

 

 

      結果必定也可想而知了吧?沒錯原來的行節點<row> 變成了咱們在PATH後面括號()中,自定義的名稱<MyHobby>,結果以下:

 

複製代碼
複製代碼
複製代碼
<MyHobby>
  <hobbyID>1</hobbyID>
  <hName>登山</hName>
</MyHobby>
<MyHobby>
  <hobbyID>2</hobbyID>
  <hName>游泳</hName>
</MyHobby>
<MyHobby>
  <hobbyID>3</hobbyID>
  <hName>美食</hName>
</MyHobby>
複製代碼
複製代碼
複製代碼

 

      這個時候細心的朋友必定又會問那麼列節點如何改變呢?還記的給列起別名的關鍵字AS嗎?對了就是用它!代碼以下:

 

SELECT hobbyID as 'MyCode',hName as 'MyName' FROM @hobby FOR XML PATH('MyHobby')

 

 

      那麼這個時候咱們列的節點名稱也會編程咱們自定義的名稱 <MyCode>與<MyName>結果以下:

複製代碼
複製代碼
複製代碼
<MyHobby>
  <MyCode>1</MyCode>
  <MyName>登山</MyName>
</MyHobby>
<MyHobby>
  <MyCode>2</MyCode>
  <MyName>游泳</MyName>
</MyHobby>
<MyHobby>
  <MyCode>3</MyCode>
  <MyName>美食</MyName>
</MyHobby>
複製代碼
複製代碼
複製代碼

    噢! 既然行的節點與列的節點咱們均可以自定義,咱們是否能夠構建咱們喜歡的輸出方式呢?仍是看代碼: 

SELECT '[ '+hName+' ]' FROM @hobby FOR XML PATH('')

 

    沒錯咱們還能夠經過符號+號,來對字符串類型字段的輸出格式進行定義。結果以下:

 

[ 登山 ][ 游泳 ][ 美食 ]

 

    那麼其餘類型的列怎麼自定義? 不要緊,咱們將它們轉換成字符串類型就行啦!例如:

 

SELECT '{'+STR(hobbyID)+'}','[ '+hName+' ]' FROM @hobby FOR XML PATH('')

 

    好的 FOR XML PATH就基本介紹到這裏吧,更多關於FOR XML的知識請查閱幫助文檔!

 

    接下來咱們來看一個FOR XML PATH的應用場景吧!那麼開始吧。。。。。。

 

        二.一個應用場景與FOR XML PATH應用

 

        首先呢!咱們在增長一張學生表,列分別爲(stuID,sName,hobby),stuID表明學生編號,sName表明學生姓名,hobby列存學生的愛好!那麼如今表結構以下:

 

           

 

        這時,咱們的要求是查詢學生表,顯示全部學生的愛好的結果集,代碼以下:

 

複製代碼
複製代碼
複製代碼
SELECT B.sName,LEFT(StuList,LEN(StuList)-1) as hobby FROM (
SELECT sName,
(SELECT hobby+',' FROM student 
  WHERE sName=A.sName 
  FOR XML PATH('')) AS StuList
FROM student A 
GROUP BY sName
) B 
複製代碼
複製代碼
複製代碼

 

         結果以下:

 

 分析: 好的,那麼咱們來分析一下,首先看這句:

 

SELECT hobby+',' FROM student 
  WHERE sName=A.sName 
  FOR XML PATH('')

 

這句是經過FOR XML PATH 將某一姓名如張三的愛好,顯示成格式爲:「 愛好1,愛好2,愛好3,」的格式!

 

那麼接着看:

 

複製代碼
複製代碼
複製代碼
SELECT B.sName,LEFT(StuList,LEN(StuList)-1) as hobby FROM (
SELECT sName,
(SELECT hobby+',' FROM student 
  WHERE sName=A.sName 
  FOR XML PATH('')) AS StuList
FROM student A 
GROUP BY sName
) B  
複製代碼
複製代碼
複製代碼

 

剩下的代碼首先是將表分組,在執行FOR XML PATH 格式化,這時當尚未執行最外層的SELECT時查詢出的結構爲:

 

 

能夠看到StuList列裏面的數據都會多出一個逗號,這時隨外層的語句:SELECT B.sName,LEFT(StuList,LEN(StuList)-1) as hobby  就是來去掉逗號,並賦予有意義的列明!

 
 

MemCahe

首先介紹下memcahce的定義:是一個分佈式的高速緩存系統,目前被許多網站使用以提高網站的訪問速度,尤爲對於一些大型的、須要頻繁訪問數據庫的網站訪問速度提高效果十分顯著。

接下來介紹下在windows下的memcache的安裝與使用:

第一步:下載memcache安裝包

連接:下載mamcache安裝包 密碼: kwt5

第二步:安裝memcache服務:

1 開啓memcache服務:首先查看本身計算機所開啓的服務----在開始菜單查詢裏輸入命令:services.msc 

接下來在dos窗口安裝memcache服務-----在開始菜單查詢裏輸入cmd指令,打開dos黑窗口

 

接下來輸入指令來開啓memcache服務

那麼這個時候刷新下服務,就會出現memcached server

談到這個地方,有些人不明白memcache 與memcached的區別,這裏簡要說下,memecache是項目名,叫分佈式緩存系統,而memcached.exe是一個程序名,只是項目中的一個啓動服務的程序。

接下來經過telnet命令,來鏈接服務器的memcache端口,往memcache裏面添加數據,進行一些簡單操做。

telnet:是TCP/IP協議族中的一員,簡單說就是能夠經過telnet指令來鏈接指定的服務器(能夠是多臺服務器),對服務器中的內容進行操做。

首先開啓telnet本機服務----打開控制面板,選擇程序和功能,打開或關閉windows功能,在Telnet客戶端上打鉤

下一步:經過telnet命令鏈接本地服務器的memcache端口

鏈接成功後的結果:

若是鏈接不成功,請檢查下telnet客戶端功能有沒有添加,memcache服務狀態是不是已啓動。

接下來開始玩一下memcache。

首先輸入stats,返回統計信息例如 PID(進程號)、版本號、鏈接數等

這裏面的參數參考:http://www.runoob.com/memcached/memcached-stats.html

接下來就是一些簡單的指令操做,具體的學習參考上面的連接。

學習到這裏,我相信基本的memcache使用應該會了。接下來寫了一個demo,也比較簡單。

首先須要下載memcache,net庫  下載地址:連接: 下載memcache,net庫 密碼: fsfw

引用三個dll文件:ICSharpCode.SharpZipLib.dll    log4net.dll     Memcached.ClientLibrary.dll

複製代碼
namespace MemCacheDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] servers = { "127.0.0.1:11211" };// 127.0.0.1:服務器地址,11211:memcache端口號
             # region 初始化池
           
            SockIOPool pool = SockIOPool.GetInstance();
            //設置服務器列表
            pool.SetServers(servers);
            //各服務器之間負載均衡的設置比例
            pool.SetWeights(new int[] { 1 });
            //初始化時建立鏈接數
            pool.InitConnections = 3;
            //最小鏈接數
            pool.MinConnections = 3;
            //最大鏈接數
            pool.MaxConnections = 5;
            //鏈接的最大空閒時間,下面設置爲6個小時(單位ms),超過這個設置時間,鏈接會被釋放掉
            pool.MaxIdle = 1000 * 60 * 60 * 6;
            //socket鏈接的超時時間,下面設置表示不超時(單位ms),即一直保持連接狀態
            pool.SocketConnectTimeout = 0;
            //通信的超時時間,下面設置爲3秒(單位ms),.Net版本沒有實現
            pool.SocketTimeout = 1000 * 3;
            //維護線程的間隔激活時間,下面設置爲30秒(單位s),設置爲0時表示不啓用維護線程
            pool.MaintenanceSleep = 30;
            //設置SocktIO池的故障標誌
            pool.Failover = true;
            //是否對TCP/IP通信使用nalgle算法,.net版本沒有實現
            pool.Nagle = false;
            //socket單次任務的最大時間(單位ms),超過這個時間socket會被強行中端掉,當前任務失敗。
            pool.MaxBusy = 1000 * 10;
            pool.Initialize();
            #endregion

            #region 客戶端實例
            MemcachedClient Cache = new MemcachedClient();
            //是否啓用壓縮數據:若是啓用了壓縮,數據壓縮長於門檻的數據將被儲存在壓縮的形式
            Cache.EnableCompression = false;
            //壓縮設置,超過指定大小的都壓縮 
            //cache.CompressionThreshold = 1024 * 1024;
            Cache.Add("myKey", "523146");//向memcache緩存裏添加數據        
            #endregion
            pool.Shutdown();//關閉鏈接池

        }
    }
}
複製代碼

對上面的代碼解釋下:首先設置鏈接的服務器,及memcache端口號   接下來初始化sock線程池    實例化memcache客戶端,添加鍵值。

運行後,能夠在dos窗口中獲取myKey值。

看到這裏,相信讀者有了一點感受了。

接下來,談談爲何使用memcache可以提升用戶的訪問速度?

假設有10萬個用戶請求網站,那麼後臺程序會到數據庫裏查詢用戶數據,將查詢到的數據放入到memcache中,這裏會爲每一個用戶隨機產生一個guid用來表示key,登陸信息的數據放在value中,而後根據guid經過hash算法產生特定的哈希值用來將用戶信息按照必定的規則存儲在memcache中。這樣子,當用戶訪問主頁的時候,首先會到memcache緩存裏查詢是否有登陸信息,而不須要到數據庫裏查詢。同時由於每一個用戶信息都按照必定的規則存放在緩存中,因此到緩存裏查詢數據時,會節省時間。

寫下這篇文章主要記錄本身學習的點點滴滴,固然這篇博客裏也參考了博友們的文章和一些視頻資料。若是讀者有什麼不明白地方,或者以爲文章有錯誤,歡迎指正,謝謝。

 

C# 操做Excel圖形——繪製、讀取、隱藏、刪除圖形

 

簡介

本篇文章將介紹C# 如何處理Excel圖形相關的問題,包括如下內容要點:

1.繪製圖形

   1.1 繪製圖形並添加文本到圖形

   1.2 添加圖片到圖形

   1.3 設置圖形陰影效果

2. 提取圖形中的文本、圖片

3. 設置圖形的顯示、隱藏

4. 刪除圖形

   4.1刪除指定圖形

   4.2 刪除全部圖形

所需工具

PS: 下載安裝該類庫後,注意在項目程序中添加引用Spire.Xls.dll文件(dll文件可在安裝路徑下的Bin文件夾中獲取)

注:Spire.xls能支持的圖形種類不少,常見的Office Excel中的圖形,這個類庫也都能實現,

示例代碼(供參考)

1. 繪製圖形

【C#】

複製代碼
using System.Drawing;
using Spire.Xls;
using Spire.Xls.Core;

namespace Add_shapes_to_Excel
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立實例
            Workbook workbook = new Workbook();
            //獲取第一個工做表
            Worksheet sheet = workbook.Worksheets[0];

            //添加「太陽」形狀的圖形,並填充顏色
            IPrstGeomShape Triangle = sheet.PrstGeomShapes.AddPrstGeomShape(2, 2, 170, 160, PrstGeomShapeType.Sun);
            Triangle.Fill.ForeColor = Color.Orange;
            Triangle.Fill.FillType = ShapeFillType.SolidColor;            
            Triangle.Text = "IT'S A SUNNY DAY";//添加文本

            //添加「禁止」標誌的圖形,並填充漸變顏色
            IPrstGeomShape heart = sheet.PrstGeomShapes.AddPrstGeomShape(2, 7, 140, 140, PrstGeomShapeType.NoSmoking);
            heart.Fill.ForeColor = Color.Red;
            heart.Fill.FillType = ShapeFillType.Gradient;

            //添加雲朵形狀的圖形
            IPrstGeomShape Cloud = sheet.PrstGeomShapes.AddPrstGeomShape(15, 2, 160, 160, PrstGeomShapeType.Cloud);           
            //設置圖形陰影效果
            Cloud.Shadow.Angle = 90;
            Cloud.Shadow.Distance = 10;
            Cloud.Shadow.Size = 100;
            Cloud.Shadow.Color = Color.SteelBlue;
            Cloud.Shadow.Blur = 30;
            Cloud.Shadow.Transparency = 1;
            Cloud.Shadow.HasCustomStyle = true;
            
            //添加五角星形狀的圖形,並加載圖片來填充圖形
            IPrstGeomShape cloud = sheet.PrstGeomShapes.AddPrstGeomShape(15, 7, 160, 160, PrstGeomShapeType.Star5);
            cloud.Fill.CustomPicture(Image.FromFile("sm.png"), "sm.png");
            cloud.Fill.FillType = ShapeFillType.Picture;
         
            //保存並打開文檔
            workbook.SaveToFile("AddShapes.xlsx", ExcelVersion.Version2013);
            System.Diagnostics.Process.Start("AddShapes.xlsx");
        }
    }
}
複製代碼

圖形插入效果:

2.提取圖形中的文本和圖片

【C#】

複製代碼
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
using Spire.Xls;
using Spire.Xls.Core;

namespace Extract_text_and_image_from_Excel_shape
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立實例,加載Excel工做表
            Workbook workbook = new Workbook();
            workbook.LoadFromFile("test.xlsx");

            //獲取第一個工做表
            Worksheet sheet = workbook.Worksheets[0];

            //提取指定形狀中的文本內容,並將提取到的文本保存到指定文檔
            IPrstGeomShape shape1 = sheet.PrstGeomShapes[0];
            string s = shape1.Text;
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(s);
            File.WriteAllText("ExtractText.txt", sb.ToString());
            System.Diagnostics.Process.Start("ExtractText.txt");

            //提取指定圖形中的圖片,並保存圖片到指定文件
            IPrstGeomShape shape2 = sheet.PrstGeomShapes[3];
            Image image = shape2.Fill.Picture;
            image.Save("ShapeImage.png", ImageFormat.Png);
            System.Diagnostics.Process.Start("ShapeImage.png");
        }
    }
}
複製代碼

提取結果:

3. 設置圖形的隱藏、顯示

【C#】

複製代碼
using Spire.Xls;

namespace HideShapes_XLS
{
    class Program
    {
        static void Main(string[] args)
        {
            //建立實例,加載Excel文檔
            Workbook workbook = new Workbook();
            workbook.LoadFromFile("test.xlsx");

            //獲取第一個工做表
            Worksheet sheet = workbook.Worksheets[0];

            //隱藏第3個圖形
            sheet.PrstGeomShapes[2].Visible = false;
            //顯示圖形
            //sheet.PrstGeomShapes[1].Visible = true;

            //保存並打開文檔
            workbook.SaveToFile("HideShape.xlsx", ExcelVersion.Version2013);
            System.Diagnostics.Process.Start("HideShape.xlsx");
        }
    }
}
複製代碼

設置效果:

4. 刪除Excel圖形

【C#】

複製代碼
using Spire.Xls;

namespace RemoveShapes_XLS
{
    class Program
    {
        static void Main(string[] args)
        {
            //實例化Workbook類對象,加載Excel文件
            Workbook workbook = new Workbook();
            workbook.LoadFromFile("test.xlsx");

            //獲取第一個工做表
            Worksheet sheet = workbook.Worksheets[0];

            //刪除第一個圖形
            sheet.PrstGeomShapes[0].Remove();

            //刪除全部圖形
            //for (int i = sheet.PrstGeomShapes.Count-1; i >= 0; i--)
            //{
            //    sheet.PrstGeomShapes[i].Remove();
            //}

            //保存並打開文件
            workbook.SaveToFile("DeleteShape.xlsx", ExcelVersion.Version2013);
            System.Diagnostics.Process.Start("DeleteShape.xlsx");
        }
    }
}
複製代碼

圖形刪除效果:

以上是關於「C#操做Excel中圖形」的介紹,如需轉載,請註明出處。

 

 

 ASP.NET MVC 是微軟官方提供的以MVC模式爲基礎的ASP.NET Web應用程序(Web Application)框架,它由Castle的MonoRail而來。

MVC 編程模式

       MVC 是三種 ASP.NET 編程模式中的一種。

       MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計建立 Web 應用程序的模式。

    (1)Model(模型)表示應用程序核心(好比數據庫記錄列表)。

    (2)View(視圖)顯示數據(數據庫記錄)。

    (3)Controller(控制器)處理輸入(寫入數據庫記錄)。

       MVC 模式同時提供了對 HTML、CSS 和 JavaScript 的徹底控制。Model(模型)是應用程序中用於處理應用程序數據邏輯的部分。一般模型對象負責在數據庫中存取數據。View(視圖)是應用程序中處理數據顯示的部分。一般視圖是依據模型數據建立的。Controller(控制器)是應用程序中處理用戶交互的部分。一般控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據。       

       新建一個ASP.NET MVC4應用程序,結構以下圖所示:

對各個文件夾的說明:

(1)App_Data 文件夾用於存儲應用程序數據。   

(2)Content 文件夾用於存放靜態文件,好比樣式表(CSS 文件)、圖標和圖像。 

(3)Controllers 文件夾包含負責處理用戶輸入和相應的控制器類。

(4)Models 文件夾包含表示應用程序模型的類。模型控制並操做應用程序的數據。

(5)Views 文件夾用於存儲與應用程序的顯示相關的 HTML 文件(用戶界面)。

(6)Scripts 文件夾存儲應用程序的 JavaScript 文件。

 下面就主要的Controller、Model和View作出說明。

1、控制器

一、描述

       控制器(Controller)主要負責響應用戶的輸入,並在響應時修改模型(Model)。經過這種方式,控制器主要關注的是應用程序流、輸入數據的處理,以及對相關視圖(View)輸出數據的提供。

二、簡單控制器

       新建一個ASP.NET MVC4應用程序,會自動生成一個HomeController和AccountController。

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
            return View();
        }
    }
}
複製代碼

  

      直接按F5運行程序便可看到index試圖中的內容,此時瀏覽器的URL爲:

       http://localhost:4573,或者修改瀏覽器地址爲:

       http://localhost:4573/home

       http://localhost:4573/home/index

       程序默認會託管在VS2013自帶的IIS中,採用的端口號爲4573。若是程序託管在MyWeb.com中,則URL應爲:

       http://MyWeb.com/home/index。

三、控制器操做中的參數

       前面的例子Action中返回的是字符串常量,下面就讓它們經過相應URL傳進來的參數動態地執行操做。

       在這裏,咱們使用HttpUtility.HtmlEncode來預處理用戶輸入。這樣就能阻止用戶用連接向視圖中注入JavaScriptd代碼或HTML標記,好比/home/sayhello?content=<script>window.location='http://www.baidu.com'</script>。

 
public string SayHello(string content)
{
    string message = HttpUtility.HtmlEncode("Hello " + content);
    return message;
}

 

  1.  
  2. public string Details(int Id)
    {
        return "ID:" + Id;
    }

     

四、路由---將URL映射到動做

       框架是如何知道將URL映射到一個特定的控制動做的?答案就在Global.asax文件的RegisterRoutes方法中。該方法定義了將一個URL模式映射到控制器或動做的路由。

複製代碼
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace MvcApplication1
{
    // 注意: 有關啓用 IIS6 或 IIS7 經典模式的說明,
    // 請訪問 http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }
    }
}
複製代碼

 

  1. 在RegisterRoutes方法上按F12,轉到該方法的定義,以下:

 

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;  
 
namespace MvcApplication1
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                                name: "Default",
                                url: "{controller}/{action}/{id}",
                                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                            );
        }
    }
}
複製代碼

   

五、控制器總結

(1)不須要作任何配置,只需瀏覽到/控制器/動做URL便可;

(2)控制器是一個很是簡單的類,繼承自System.Web.Mvc.Controller類;

(3)用控制器在瀏覽器中顯示文本,沒有用到View或Model。

2、視圖

一、做用

       提供用戶界面。一個控制器能夠對應多個試圖,一個視圖能夠被多個控制器使用。

       在Action名上右鍵→添加試圖→View1。

二、指定視圖

複製代碼
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>View1</title>
</head>
<body>
    <div>
        <h2>@ViewBag.Message</h2>
    </div>
</body>
</html>
複製代碼

三、ViewData和ViewBag的區別於聯繫

       在前面的例子中,使用了ViewBag的Message屬性從控制器往視圖傳遞數據,ViewData是一個特殊的字典類,能夠按標準語法進行使用:ViewData["CurrentTime"]=DateTime.Now;

       這種語法也能夠用動態封裝器ViewBag:ViewBag.CurrentTime=DateTime.Now;

注意:

(1)儘管只有當要訪問的關鍵字是有效的C#標識符是,ViewBag才起做用,如在ViewData["Key With Spaces"]就不能使用ViewBag訪問,編譯不經過;

(2)動態值不能做爲一個參數傳遞給擴展方法。由於C#編譯器爲了選擇正確的擴展方法,在編譯是必須知道每一個參數真正類型。如:@Html.TextBox("name",ViewBag.Name)不會編譯經過,能夠改成:

       ①@Html.TextBox("name",ViewData["Name"])

       ②@Html.TextBox("name",(string)ViewBag.Name)

四、強類型視圖

       如今須要編寫一個顯示Album實例列表的視圖。一簡單的方法就是經過ViewBag屬性把那些Album實例添加到視圖數據字典中,而後在視圖中迭代他們。

(1)首先,在Models文件夾下新建一個Album類,爲了簡單起見,只定義一個Title屬性。

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplication1.Models
{
    public class Album
    {
        public string Title { get; set; }
    }
}
複製代碼
  1. (2)控制器
複製代碼
public ActionResult List()
{
    var album = new List<Album>();
    for (int i = 0; i < 10; i++)
    {
        album.Add(new Album { Title = "Product " + i.ToString() });
    }
    //1、使用ViewBag傳值
    //ViewBag.Album = album;
    //return View("ListView");
    //2、使用ViewData傳值
    ViewData["Album"] = album;
    return View("ListView");
}
複製代碼

  

(3)視圖

       在List上右鍵→添加視圖。

複製代碼
@{
    Layout = null;
}
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @*1、使用ViewBag傳值*@
            @*@foreach(MvcApplication1.Models.Album a in (ViewBag.Album as IEnumerable
            <MvcApplication1.Models.Album>))
            {
                <li>@a.Title</li>
            }*@
            @*2、使用ViewBag傳值*@

            @foreach (MvcApplication1.Models.Album a in (ViewData["Album"] as IEnumerable<MvcApplication1.Models.Album>))
            {
                <li>@a.Title</li>
            }

        </ul>
        <pre name="code" class="html">
        <ul>
            @foreach(dynamic d in ViewBag.Album)
            {
                <li>@d.Title</li>
            }
 
        </ul>
    </div>
</body>
</html>
複製代碼

運行效果:

 
           
       注意,在枚舉以前須要動態的將ViewBag.Album轉換爲IEnumerable<Album>類型,爲了使代碼乾淨整潔,也可使用dynamic關鍵字。
<ul>
    @foreach (dynamic d in ViewBag.Album)
    {
        <li>@d.Title</li>
    }
</ul> 

       請記住,ViewData是ViewDataDictionary類型的,它有一個額外的Model屬性,能夠用來在視圖中獲取指定的模型對象。因爲在ViewData中只能傳遞一個模型對象,所以,咱們利用這一點能夠很容易的實現向視圖傳遞一個特定的類對象。

       在Controller方法中,能夠經過重載的List方法中傳遞模型實例來指定模型,代碼以下:

複製代碼
public ActionResult List()
{
    var album = new List<Album>();
    for (int i=0;i < 10; i++) 
    {
        album.Add(new Album { Title="Product " + i.ToString() });
    }
    ViewData["Album"]=album;
    return View("ListView",album);
}

@model IEnumerable<MvcApplication1.Models.Album>

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @foreach(dynamic d in Model)
            {
                <li>@d.Title</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

       若是不想輸入模型類型的徹底限定類型名,可以使用using關鍵字聲明,以下所示:

 

複製代碼
@using MvcApplication1.Models

@model IEnumerable<Album>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @foreach (dynamic d in Model)
            {
                <li>@d.Title</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

       對於在視圖中常用的名稱空間,一個較好的方法就是在Views目錄下的web.config文件中聲明。

 

複製代碼
<system.web.webPages.razor>
    <host factoryType = "System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    < pages pageBaseType="System.Web.Mvc.WebViewPage">
        <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization"/>
        <add namespace="System.Web.Routing" />
        <add namespace="MvcApplication1.Models"/>
        </namespaces>
    </pages>
</system.web.webPages.razor>
複製代碼
 

五、Razor視圖引擎

5.1 先來看一個簡單的例子。

複製代碼
@{
    Layout = null;
}

@{
    var items = new string[] { "one", "two", "three" };
}

<!DOCTYPE html>
<html>
<head>
    <title>ListView</title>
</head>

<body>
    <div>
        <ul>
            @foreach (var item in items)
            {
                <li>@item</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

 

5.2 Html編碼

       由於在不少狀況下須要用視圖顯示用戶輸入,如博客評論等,因此老是存在着潛在的跨站腳本注入攻擊(也成XSS),值得稱讚的是,Razor表達式是Html自動編碼的,以下不會彈出一個警示框,而是直接顯示html代碼。

 

@{
    string message = "<script>alert('haacked!');</script>";
}

 

        然而,若是想要展現Html標籤,就要返回一個System.Web.IHtml對象的實例,Razor並不對它進行編碼。固然也能夠建立一個HtmlStringl實例或者使用Html.Raw便捷方法。

 

@{

    string message = "<script>alert('haacked!');</script>";

}
<span>@Html.Raw(message)</span>

  

  1. 效果:

 

 

       雖然這種自動的HTML編碼經過對一HTML形式顯示的用戶輸入進行編碼能有效的緩和XSS的脆弱性,可是對於在JavaScript中時遠遠不夠的。例如:

 

複製代碼
<script type="text/javascript">
    $(function ()
    {
        var message = 'Hello @Ajax.JavaScriptStringEncode(ViewBag.Message)';
        $("#message").html(message).show('slow');

    });
</script>
複製代碼

 

  1.        當在JavaScript中將用戶提供的值賦給變量時,要使用JavaScript字符串編碼而不只僅是HTML編碼,記住這一點是很重要的,也就是要用@Ajax.JavaScriptStringEncode方法對用戶輸入進行編碼。

5.3 佈局

       Razor的佈局有助於使用應用程序中的多個視圖保持一致的外觀,可以使用佈局爲網站定義公共模板(或只是其中的一部分),公共模板包含一個或多個佔位符,應用程序中的其餘視圖爲他們提供內容。

       下面看一個很是簡單的佈局,新建一個名稱爲MyLayout.cshtml的視圖,因爲要做爲公共模板,因此將其放在/Views/Shared/路徑下。

 

複製代碼
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>
<body>
    <h1>@ViewBag.Title</h1>
    <div id="Container">@RenderBody()</div>
</body>
</html>
複製代碼

 

 

 

  1.        其中的@RenderBody()稱爲佔位符,用來標記使用這個模板的視圖將渲染他們的主要內容的位置。

 

       接下來新建一個視圖,將使用其做爲模板。

 

@{
    Layout = "~/Views/Shared/MyLayout.cshtml";
    ViewBag.Title = "GoodLuck";
}
<p>This is the main content</p>

 

 

 

 

  1. 運行效果以下:

       佈局可能有多個節,例以下面示例在前面的佈局基礎上添加了一個頁腳節:

 

複製代碼
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>
<body>
    <h1>@ViewBag.Title</h1>
    <div id="Container">@RenderBody()</div>
    <footer>@RenderSection("Footer")</footer>
</body>
</html>
複製代碼

 

 

 

 

  1.        在不作任何改變的狀況下再次運行前面的視圖,將會拋出一個異常,提示沒有定義的Footer節。

 

       默認狀況下,視圖必須爲佈局中定義的沒一個節提供相應的內容。更新後的View1視圖以下所示:

 

複製代碼
@{
    Layout = "~/Views/Shared/MyLayout.cshtml";
    ViewBag.Title = "GoodLuck";
}

<p>This is the main content</p>

@section Footer{
    This is the <strong>footer</strong>.
}
複製代碼

 

 

 

  1.  

       RenderSection方法有一個重載的版本,容許在佈局中指定不須要的節,能夠給required參數傳遞一個false值來標記Footer節是可選的:

 

複製代碼
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>

<body>
    <h1>@ViewBag.Title</h1>
    <div id="Container">@RenderBody()</div>
    <footer>@RenderSection("Footer", required: false)</footer>
</body>
</html>
複製代碼

 

 

 

 



5.4 ViewStart

       在前面的例子中,每個視圖都是用Layout屬性來指定它的佈局,若是多個視圖使用同一個佈局,就會產生冗餘,而且很難維護。

       _ViewStart.cshtml頁面可用來消除這種冗餘,這個文件的代碼先於同目錄下任何視圖代碼的執行,這個文件也能夠遞歸地應用到子目錄下的任何視圖。

@{

Layout="~/Views/Shared/_Layout.cshtml";

}

       由於這個代碼先於任何視圖執行,因此一個視圖能夠重寫Layout屬性的默認值,從而從新選擇一個不一樣的佈局。

5.4 指定部分視圖

       除了返回視圖以外,操做方法也能夠經過PartialView方法以PartialResult的形式返回部分視圖。

The end

ASP.NET MVC 是微軟官方提供的以MVC模式爲基礎的ASP.NET Web應用程序(Web Application)框架,它由Castle的MonoRail而來。

MVC 編程模式

       MVC 是三種 ASP.NET 編程模式中的一種。

       MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計建立 Web 應用程序的模式。

    (1)Model(模型)表示應用程序核心(好比數據庫記錄列表)。

    (2)View(視圖)顯示數據(數據庫記錄)。

    (3)Controller(控制器)處理輸入(寫入數據庫記錄)。

       MVC 模式同時提供了對 HTML、CSS 和 JavaScript 的徹底控制。Model(模型)是應用程序中用於處理應用程序數據邏輯的部分。一般模型對象負責在數據庫中存取數據。View(視圖)是應用程序中處理數據顯示的部分。一般視圖是依據模型數據建立的。Controller(控制器)是應用程序中處理用戶交互的部分。一般控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據。       

       新建一個ASP.NET MVC4應用程序,結構以下圖所示:

對各個文件夾的說明:

(1)App_Data 文件夾用於存儲應用程序數據。   

(2)Content 文件夾用於存放靜態文件,好比樣式表(CSS 文件)、圖標和圖像。 

(3)Controllers 文件夾包含負責處理用戶輸入和相應的控制器類。

(4)Models 文件夾包含表示應用程序模型的類。模型控制並操做應用程序的數據。

(5)Views 文件夾用於存儲與應用程序的顯示相關的 HTML 文件(用戶界面)。

(6)Scripts 文件夾存儲應用程序的 JavaScript 文件。

 下面就主要的Controller、Model和View作出說明。

1、控制器

一、描述

       控制器(Controller)主要負責響應用戶的輸入,並在響應時修改模型(Model)。經過這種方式,控制器主要關注的是應用程序流、輸入數據的處理,以及對相關視圖(View)輸出數據的提供。

二、簡單控制器

       新建一個ASP.NET MVC4應用程序,會自動生成一個HomeController和AccountController。

 

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
            return View();
        }
    }
}
複製代碼

 



       直接按F5運行程序便可看到index試圖中的內容,此時瀏覽器的URL爲:

 

       http://localhost:4573,或者修改瀏覽器地址爲:

       http://localhost:4573/home

       http://localhost:4573/home/index

       程序默認會託管在VS2013自帶的IIS中,採用的端口號爲4573。若是程序託管在MyWeb.com中,則URL應爲:

       http://MyWeb.com/home/index。

三、控制器操做中的參數

       前面的例子Action中返回的是字符串常量,下面就讓它們經過相應URL傳進來的參數動態地執行操做。

       在這裏,咱們使用HttpUtility.HtmlEncode來預處理用戶輸入。這樣就能阻止用戶用連接向視圖中注入JavaScriptd代碼或HTML標記,好比/home/sayhello?content=<script>window.location='http://www.baidu.com'</script>。

 

public string SayHello(string content)
{
    string message = HttpUtility.HtmlEncode("Hello " + content);
    return message;
}

 

 

 



public string Details(int Id)
{
    return "ID:" + Id;
}

 

 

 

四、路由---將URL映射到動做

       框架是如何知道將URL映射到一個特定的控制動做的?答案就在Global.asax文件的RegisterRoutes方法中。該方法定義了將一個URL模式映射到控制器或動做的路由。

 

複製代碼
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace MvcApplication1
{
    // 注意: 有關啓用 IIS6 或 IIS7 經典模式的說明,
    // 請訪問 http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }
    }
}
複製代碼

 

 

 



在RegisterRoutes方法上按F12,轉到該方法的定義,以下:

 

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace MvcApplication1
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                                name: "Default",
                                url: "{controller}/{action}/{id}",
                                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                            );
        }
    }
}
複製代碼

 

 

 



五、控制器總結

 

(1)不須要作任何配置,只需瀏覽到/控制器/動做URL便可;

(2)控制器是一個很是簡單的類,繼承自System.Web.Mvc.Controller類;

(3)用控制器在瀏覽器中顯示文本,沒有用到View或Model。

2、視圖

一、做用

       提供用戶界面。一個控制器能夠對應多個試圖,一個視圖能夠被多個控制器使用。

       在Action名上右鍵→添加試圖→View1。

二、指定視圖

 

複製代碼
@{
     Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>View1</title>
</head>
<body>
    <div>
        <h2>@ViewBag.Message</h2>
    </div>
</body>
</html>
複製代碼

 

 

 



三、ViewData和ViewBag的區別於聯繫

       在前面的例子中,使用了ViewBag的Message屬性從控制器往視圖傳遞數據,ViewData是一個特殊的字典類,能夠按標準語法進行使用:ViewData["CurrentTime"]=DateTime.Now;

       這種語法也能夠用動態封裝器ViewBag:ViewBag.CurrentTime=DateTime.Now;

注意:

(1)儘管只有當要訪問的關鍵字是有效的C#標識符是,ViewBag才起做用,如在ViewData["Key With Spaces"]就不能使用ViewBag訪問,編譯不經過;

(2)動態值不能做爲一個參數傳遞給擴展方法。由於C#編譯器爲了選擇正確的擴展方法,在編譯是必須知道每一個參數真正類型。如:@Html.TextBox("name",ViewBag.Name)不會編譯經過,能夠改成:

       ①@Html.TextBox("name",ViewData["Name"])

       ②@Html.TextBox("name",(string)ViewBag.Name)

四、強類型視圖

       如今須要編寫一個顯示Album實例列表的視圖。一簡單的方法就是經過ViewBag屬性把那些Album實例添加到視圖數據字典中,而後在視圖中迭代他們。

(1)首先,在Models文件夾下新建一個Album類,爲了簡單起見,只定義一個Title屬性。

 

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MvcApplication1.Models
{
    public class Album
    {
        public string Title { get; set; }
    }
}
複製代碼

 

 

 



(2)控制器

 

複製代碼
public ActionResult List()
{
    var album = new List<Album>();
    for (int i=0;i < 10 ;i++) 
    {
        album.Add(new Album { Title="Product " + i.ToString() });
    }

    //1、使用ViewBag傳值
    //ViewBag.Album = album;
    //return View("ListView");

    //2、使用ViewData傳值
    ViewData["Album"] = album;
    return View("ListView");
}
複製代碼

 

 

 



(3)視圖

       在List上右鍵→添加視圖。

 

複製代碼
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @*1、使用ViewBag傳值*@
            @*@foreach(MvcApplication1.Models.Album a in (ViewBag.Album as IEnumerable<MvcApplication1.Models.Album>))
            {
                <li>@a.Title</li>
            }*@

            @*2、使用ViewBag傳值*@
            @foreach (MvcApplication1.Models.Album a in (ViewData["Album"] as IEnumerable<MvcApplication1.Models.Album>))
            {
                <li>@a.Title</li>
            }
        </ul>

        <pre name="code" class="html">

        <ul>
            @foreach(dynamic d in ViewBag.Album)
            {
                <li>@d.Title</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

 

 

 



運行效果:

 

 
           
       注意,在枚舉以前須要動態的將ViewBag.Album轉換爲IEnumerable<Album>類型,爲了使代碼乾淨整潔,也可使用dynamic關鍵字。
<ul>
    @foreach (dynamic d in ViewBag.Album)
    {
        <li>@d.Title</li>
    }
</ul>

 



       請記住,ViewData是ViewDataDictionary類型的,它有一個額外的Model屬性,能夠用來在視圖中獲取指定的模型對象。因爲在ViewData中只能傳遞一個模型對象,所以,咱們利用這一點能夠很容易的實現向視圖傳遞一個特定的類對象。

       在Controller方法中,能夠經過重載的List方法中傳遞模型實例來指定模型,代碼以下:

 

複製代碼
public ActionResult List()
{
    var album = new List<Album>();
    for (int i=0;i < 10;i++) 
    {
        album.Add(new Album { Title="Product " + i.ToString() });
    }
    ViewData["Album"]=album;
    return View("ListView",album);
}

@model IEnumerable<MvcApplication1.Models.Album>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @foreach(dynamic d in Model)
            {
                <li>@d.Title</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

 

 

 



       若是不想輸入模型類型的徹底限定類型名,可以使用using關鍵字聲明,以下所示:

 

複製代碼
@using MvcApplication1.Models
@model IEnumerable<Album>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @foreach (dynamic d in Model)
            {
                <li>@d.Title</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

 

 

 



       對於在視圖中常用的名稱空間,一個較好的方法就是在Views目錄下的web.config文件中聲明。

 

複製代碼
<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
        <namespaces>
            <add namespace="System.Web.Mvc" />
            <add namespace="System.Web.Mvc.Ajax" />
            <add namespace="System.Web.Mvc.Html" />
            <add namespace="System.Web.Optimization" />
            <add namespace="System.Web.Routing" />
            <add namespace="MvcApplication1.Models" />
        </namespaces>
    </pages>
</system.web.webPages.razor>
複製代碼

 

 

 



五、Razor視圖引擎

5.1 先來看一個簡單的例子。

 

複製代碼
@{
    Layout = null;
}

@{
    var items = new string[] { "one", "two", "three" };
}
<!DOCTYPE html>

<html>
<head>
    <title>ListView</title>
</head>
<body>
    <div>
        <ul>
            @foreach (var item in items)
            {
                <li>@item</li>
            }
        </ul>
    </div>
</body>
</html>
複製代碼

 

 

 



5.2 Html編碼

       由於在不少狀況下須要用視圖顯示用戶輸入,如博客評論等,因此老是存在着潛在的跨站腳本注入攻擊(也成XSS),值得稱讚的是,Razor表達式是Html自動編碼的,以下不會彈出一個警示框,而是直接顯示html代碼。

 

@{
    string message = "<script>alert('haacked!');</script>";
}

<span>@message</span>

 

 

 



       然而,若是想要展現Html標籤,就要返回一個System.Web.IHtml對象的實例,Razor並不對它進行編碼。固然也能夠建立一個HtmlStringl實例或者使用Html.Raw便捷方法。

 

@{
    string message = "<script>alert('haacked!');</script>";
}

<span>@Html.Raw(message)</span>

 

 

 

  1. 效果:

 

 

       雖然這種自動的HTML編碼經過對一HTML形式顯示的用戶輸入進行編碼能有效的緩和XSS的脆弱性,可是對於在JavaScript中時遠遠不夠的。例如:

 

複製代碼
<script type="text/javascript">
    $(function() 
    {
        var message = 'Hello @Ajax.JavaScriptStringEncode(ViewBag.Message)';
        $("#message").html(message).show('slow');
    });
</script>
複製代碼

 

 

 

       當在JavaScript中將用戶提供的值賦給變量時,要使用JavaScript字符串編碼而不只僅是HTML編碼,記住這一點是很重要的,也就是要用@Ajax.JavaScriptStringEncode方法對用戶輸入進行編碼。

5.3 佈局

       Razor的佈局有助於使用應用程序中的多個視圖保持一致的外觀,可以使用佈局爲網站定義公共模板(或只是其中的一部分),公共模板包含一個或多個佔位符,應用程序中的其餘視圖爲他們提供內容。

       下面看一個很是簡單的佈局,新建一個名稱爲MyLayout.cshtml的視圖,因爲要做爲公共模板,因此將其放在/Views/Shared/路徑下。

 

複製代碼
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>
<body>
    <h1>@ViewBag.Title</h1>
    <div id="Container">@RenderBody()</div>
</body>
</html>
複製代碼

 

 

 

  1.        其中的@RenderBody()稱爲佔位符,用來標記使用這個模板的視圖將渲染他們的主要內容的位置。

 

       接下來新建一個視圖,將使用其做爲模板。

 

@{
    Layout = "~/Views/Shared/MyLayout.cshtml";
    ViewBag.Title = "GoodLuck";
}

<p>This is the main content</p>

 

 

 

運行效果以下:

       佈局可能有多個節,例以下面示例在前面的佈局基礎上添加了一個頁腳節:

 

複製代碼
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>

<body>
    <h1>@ViewBag.Title</h1>
    <div id="Container">@RenderBody()</div>
    <footer>@RenderSection("Footer")</footer>
</body>
</html>
複製代碼

 

 

 

 

  1.        在不作任何改變的狀況下再次運行前面的視圖,將會拋出一個異常,提示沒有定義的Footer節。

 

       默認狀況下,視圖必須爲佈局中定義的沒一個節提供相應的內容。更新後的View1視圖以下所示:

 

複製代碼
@{
    Layout = "~/Views/Shared/MyLayout.cshtml";
    ViewBag.Title = "GoodLuck";
}

    <p>This is the main content</p>
    @section Footer{
    This is the <strong>footer</strong>
}
複製代碼

 

 

 

 

       RenderSection方法有一個重載的版本,容許在佈局中指定不須要的節,能夠給required參數傳遞一個false值來標記Footer節是可選的:

 

複製代碼
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
</head>
<body>
    <h1>@ViewBag.Title</h1>
    <div id="Container">@RenderBody()</div>
    <footer>@RenderSection("Footer", required: false)</footer>

</body>
</html>
複製代碼

 

5.4 ViewStart

       在前面的例子中,每個視圖都是用Layout屬性來指定它的佈局,若是多個視圖使用同一個佈局,就會產生冗餘,而且很難維護。

       _ViewStart.cshtml頁面可用來消除這種冗餘,這個文件的代碼先於同目錄下任何視圖代碼的執行,這個文件也能夠遞歸地應用到子目錄下的任何視圖。

@{

Layout="~/Views/Shared/_Layout.cshtml";

}

       由於這個代碼先於任何視圖執行,因此一個視圖能夠重寫Layout屬性的默認值,從而從新選擇一個不一樣的佈局。

5.4 指定部分視圖

       除了返回視圖以外,操做方法也能夠經過PartialView方法以PartialResult的形式返回部分視圖。

The end

 

 

IOC,DIP,DI,IoC容器

Posted on  2018-07-30 17:34 一個老年人 閱讀(58) 評論(0) 編輯 收藏

 定義

IOC(Inversion of Control  控制反轉),DIP(Dependency Inverson Principle 依懶倒置)都屬於設計程序時指導原則,並無具體的實現。比較經常使用的五大原則SOLID(SRP單一職責、OCP開閉原則、LSP里氏轉換原則、IOC、DIP)

DI(Dependency Injection 依懶注入)屬於模式,提供了一種具體的處理程序中對應狀況的實現

IoC容器  屬於一種框架  例如

我只會autofac,屬於菜鳥,歡迎交流

 

IoC原則:反轉控制(通常與DIP一塊兒使用)

用代碼演示一下

複製代碼
public class A
    {
        public void Task()
        {
            var b=new B();
            b.DoSomeThing();
        }
    }

    public class B
    {
        public void DoSomeThing()
        {

        }
    }
複製代碼

上面的代碼,

類A建立和管理類B的對象的生命週期。它控制依賴類對象的建立和生命週期。

IoC原則建議反轉控制,意味着將控制內容分離到另外一個類。換句話說,將依賴關係建立控件從A類反轉到另外一個類,以下所示。

複製代碼
public class A
    {
        public void Task()
        {
            var b=Factory.GetB();
            b.DoSomeThing();
        }
    }

    public class B
    {
        public void DoSomeThing()
        {

        }
    }
複製代碼

 A類不直接建立B類,而是經過一個工廠建立。咱們就實現了控制反轉

DIP原則:高級模塊不該該依懶低級模塊,二者都應該依懶抽象;抽象不該該依懶具體,具體應該依懶抽象

相關文章
相關標籤/搜索