背景介紹:php
爲了平衡社區成員的貢獻和索取,一塊兒幫引入了幫幫幣。當用戶積分(幫幫點)達到必定數額以後,就會「掉落」必定數量的「幫幫幣」。爲了增長趣味性,幫幫幣「掉落」以後全部用戶均可以「撿取」,誰先撿到歸誰。css
但這樣就產生了一個問題,由於這個「幫幫幣」是能夠買賣有價值的,因此不免會有惡意用戶用爬蟲不斷的掃描,致使這樣的狀況出現:html
注:經覈實,喬布斯的同窗 其實沒有用爬蟲,就是手工點,點出來的!還能說什麼呢?只能表示佩服啊佩服……jquery
因此咱們須要一種機制,阻止這種爬蟲的行爲。c++
大體思路:git
這個問題咱們有一個很便利的前提:只有註冊用戶纔可以「撿起」幫幫幣。因此,咱們不須要經過「封IP」(需獲取真實IP)這種方式來阻斷爬蟲爬行,而是直接封註冊用戶,很是方便。程序員
那麼如何判斷一個請求是真實用戶,仍是爬蟲呢?咱們決定使用最簡單的方法:記錄訪問頻次。當某一個用戶的訪問頻次高於設定值時(好比:5分鐘10次),就斷定該用戶「有爬蟲嫌疑」。github
此外,爲了防止誤判(確實有用戶手快),咱們還應該給用戶一個「解鎖」的功能:經過輸入驗證碼來肯定不是爬蟲。ajax
細節設計:redis
一個最核心的問題是:用什麼來記錄用戶的訪問頻次?
數據庫?感受不必,這個數據又不須要長期保留,訪問一次就作一次I/O操做在性能上接受不了,因此咱們決定使用內存。
可是,具體須要記錄那些數據,又用什麼樣的數據結構呢?
最後咱們選擇使用緩存,記錄最簡單的「用戶ID -> 訪問次數」鍵值對,來解決這個問題,由於:
- 利用緩存的自動清除(expire)特性,清除過時數據,保證記錄的訪問次數始終是在必定時間內的。
- 緩存的讀寫速度很快,性能上沒有壓力
固然,這裏其實仍是有那麼點問題的。好比,假設緩存時間是5分鐘,最多訪問次數是10次。0:10,開始緩存訪問次數,一直累加,到0:14,共記錄訪問次數7次,沒有問題;然而,一過0:15,緩存被清空,0:16的時候,緩存裏只有0:15到0:16這一分鐘的數據,沒有過去5分鐘(從0:11到0:16)的數據。因此用戶能夠控制一直爬蟲,訪問9次,而後就歇着,5分鐘事後,再繼續訪問9次,而後再歇5分鐘……
唉~~真這麼拼,我還真沒什麼辦法?但若是這麼一個頻次他能接受的話,我其實也無所謂,你就慢慢爬唄。或者,咱們後臺作更大的監控,把每一個用戶的每次訪問都記錄下來,進行統計,找出異常。那時候可能就真的須要數據庫了(爲了提升性能能夠內存裏放一個DataTable,定時同步到Database)。但暫時來講,沒有這個必要。
此外,還有一個問題,是否是隻須要記錄用戶訪問頻次?
若是按上述方案,在緩存裏記錄訪問頻次,經過緩存數據來判斷是否容許繼續訪問,會有一個問題:緩存到期失效以後,這個用戶就又能夠自由訪問目標頁面了!至關於到期自動解鎖。
我以爲這仍是不科學,若是認定是爬蟲,只能是人工解鎖(識別碼驗證)。因此在數據庫用戶表裏添加一個「已鎖定」(Locked)字段,若是用戶被鎖定,Update其爲當前時間;未鎖定時(解鎖後)爲NULL。
具體實現:
爲了重用,咱們須要利用 Authorize Fitler,在它的OnAuthorization()方法裏面進行檢查和記錄。
代碼自己應該比較簡單,if...else...的邏輯:
///1. 先根據數據庫撿查當前用戶是否被鎖定 ///2. 若是被鎖定,直接攔截。不然: ///3. 在緩存中檢查有無當前用戶的訪問次數記錄 /// 3.1 沒有,新建一條他的緩存。不然: /// 3.2 檢查該用戶已訪問次數 /// 3.2.1 若是已到達訪問次數限制,攔截並在數據庫中鎖定該用戶。不然 /// 3.2.2 累加用戶的訪問次數
精簡註釋代碼以下:
public class NeedLogOn : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { HttpContextBase context = filterContext.HttpContext; ///Autofac相關操做,獲取正取的ISharedService實例 ISharedService service = AutofacConfig.Container.Resolve<ISharedService>(); _NavigatorModel model = service.Get(); //從數據庫獲取當前User的信息 ///截斷式編程,減小if...else的{}嵌套 if (model.Locked.HasValue) { ///model.Locked 來自數據庫,用戶已經被鎖定,攔截 visitTooMuch(filterContext); return; } string cacheKey = CacheKey.MAX_VISIT + model.Id; ///很是有意思,不能直接使用int值類型,必須使用引用類型的 VisitCounter amount; if (context.Cache[cacheKey] == null) { amount = new VisitCounter { Value = 1 }; ///新創建一條Cache context.Cache.Add(cacheKey, amount, null, DateTime.Now.AddSeconds(Config.Seconds), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null); } else { amount = context.Cache[cacheKey] as VisitCounter; if (amount.Value >= Config.MaxVisit) { ///在數據庫中鎖定該用戶 service.LockCurrentUser(); BaseService.Commit(); ///當即清除Cache context.Cache.Remove(cacheKey); visitTooMuch(filterContext); return;} else { ///不能使用:currentVisitAmount++; ///context.Cache[cacheKey] = currentVisitAmount; ///見:https://stackoverflow.com/questions/2118067/cached-item-never-expiring amount.Value++; } } } } public class VisitCounter { public int Value { get; set; } }
仔細觀察代碼,你會發現兩個問題。這就是飛哥我曾經掉的坑啊!o(╥﹏╥)o
一、爲何要引入VisitCounter類?
緩存裏就存放着這個類的實例,而這個類其實就包裹一個int Value;幹嗎呢,這是?爲何不直接用int呢?直接把int存到Cache裏不行嗎?
不行啊!艹。
存進去,沒問題;取出來,也沒問題;但更新(累加)的時候有問題啊。你怎麼更新?
//取出緩存 currentVisitAmount = Convert.ToInt32(context.Cache[cacheKey]); //累加 currentVisitAmount++; //再存進去 context.Cache[cacheKey] = currentVisitAmount;
這樣不行的,具體的解釋看這裏:Cached item never expiring。
簡單的說,context.Cache[cacheKey] = currentVisitAmount; 這一句,等於從新插入了一條永不過時的緩存。萬萬沒想到啊!這個bug把飛哥都差點搞瘋了,原本cache的調試都很是麻煩,還搞個這種幺蛾子。
因此解決的辦法是什麼呢?在Cache裏存一個引用類型值,而後不改Cache,只改引用類實例裏的值就OK了。代碼就不重複了。
二、在鎖定用戶的同時,清除該用戶的cache
這裏啊,曾經走了點彎路。
我最開始是在解鎖用戶的時候清除該用戶的Cache。
[NeedLogOn] public ActionResult Unlock() { string userId = getCurrentUserId(); string cacheKey = CacheKey.MAX_VISIT + userId; HttpContext.Cache.Remove(cacheKey); return View(new ImageCodeModel()); }
結果不知道咋回事,時靈時不靈。我把本地代碼,鏈接服務器數據庫,開着Debug模式,一步一會的進去看,OK,沒問題;但把本地代碼發佈到服務器,duang,不行了?!無法調試,只有寫log啥的,坑得我不要不要的……
後來忽然發現,這裏有「壞代碼的味道」:重複。你看這個cacheKey的構建,是否是在 NeedLogOn.OnAuthorization()裏構建過一次?重複使用的代碼是否是就應該封裝?因此呢,開始呢,是想弄一個方法出來得到cacheKey,好比striing GetVisitLimitCacheKey()啥的,但這個方法要讓Controller裏的UnLock()和Filter裏的OnAuthorization()都能調用,放在哪裏呢?
忽然靈光一閃:爲何 Cache.Remove 要寫在UnLock()裏面呢?
其實只要用戶被鎖定,他的緩存信息就沒用了。由於咱們已經在數據庫中標明瞭他被Locked,因此NeedLogOn.OnAuthorization()攔截住他,不須要Cache呀!儘早的清除這個Cache,還能提升那麼一點點的性能。
最關鍵的是,這樣代碼更緊湊了:cacheKe在同一個方法裏被使用,cache操做在同一個方法類完成,避免了代碼分散耦合,優雅多了!
C#中緩存的使用
緩存的概念及優缺點在這裏就很少作介紹,主要介紹一下使用的方法。
1.在ASP.NET中頁面緩存的使用方法簡單,只須要在aspx頁的頂部加上一句聲明便可:
<%@ OutputCache Duration="100" VaryByParam="none" %>
Duration:緩存時間(秒爲單位),必填屬性
2.使用微軟自帶的類庫System.Web.Caching
新手接觸的話不建議直接使用微軟提供的類庫,由於這樣對理解不夠深入。因此在這裏我帶你們本身寫一套緩存操做方法,這樣理解得更加清晰。
話很少說,代碼開敲。
1、首先,先模擬數據來源。新建一個類,寫一個數據操做方法(該方法耗時、耗資源)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading;
using
System.Threading.Tasks;
namespace
Cache
{
public
class
DataSource
{
/// <summary>
/// 模擬從數據庫讀取數據
/// 耗時、耗CPU
/// </summary>
/// <param name="count"></param>
public
static
int
GetDataByDB(
int
count)
{
Console.WriteLine(
"-------GetDataByDB-------"
);
int
result = 0;
for
(
int
i = count; i < 99999999; i++)
{
result += i;
}
Thread.Sleep(2000);
return
result;
}
}
}
|
2、編寫一個緩存操做類
2.1 構造一個字典型容器,用於存放緩存數據,權限設爲private ,防止隨意訪問形成數據不安全性
//緩存容器 private static Dictionary<string, object> CacheDictionary = new Dictionary<string, object>();
2.2 構造三個方法(添加數據至緩存容器、從緩存容器獲取數據、判斷緩存是否存在)
/// <summary> /// 添加緩存 /// </summary> public static void Add(string key, object value) { CacheDictionary.Add(key, value); } /// <summary> /// 獲取緩存 /// </summary> public static T Get<T>(string key) { return (T)CacheDictionary[key]; } /// <summary> /// 判斷緩存是否存在 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool Exsits(string key) { return CacheDictionary.ContainsKey(key); }
3、程序入口編寫測試方法
3.1 先看一下普通狀況不適用緩存,它的執行效率有多慢
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Cache { class Program { static void Main(string[] args) { for (int i = 1; i < 6; i++) { Console.WriteLine($"------第{i}次請求------"); int result = DataSource.GetDataByDB(666); Console.WriteLine($"第{i}次請求得到的數據爲:{result}"); } } } }
3.2 接下來,咱們編寫緩存試用方法。概念無非就是根據key前往字典容器裏查找是否有相對應緩存數據,有則直接調用,沒有則生成並存入字典容器裏。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Cache { class Program { static void Main(string[] args) { for (int i = 1; i < 6; i++) { Console.WriteLine($"------第{i}次請求------"); //int result = DataSource.GetDataByDB(666); int result = 0; //key的名字必定要確保請求的準確性 DataSource GetDataByDB 666缺一不可 string key = "DataSource_GetDataByDB_666"; if (CacheHelper.Exsits(key)) { //緩存存在,直接獲取原數據 result = CacheHelper.Get<int>(key); } else { //緩存不存在,去生成緩存,並加入容器 result = DataSource.GetDataByDB(666); CacheHelper.Add(key, result); } Console.WriteLine($"第{i}次請求得到的數據爲:{result}"); } } } }
3.3 咱們看看加入緩存以後的效率如何
4、能夠看到,瞬間完成。事已至此,緩存的使用基本是完成了。可是回過頭來咱們想一想看。一個系統成百上千個地方使用緩存的話,那豈不是要寫成百上千個if else判斷緩存是否存在,而後獲取?
答案顯而易見,確定不合理的。因此咱們要對代碼進行優化。
4.1 緩存操做類(CacheHelper)編寫一個通用的獲取方法
/// <summary> /// 緩存獲取方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key">緩存字典容器對應key</param> /// <param name="func">委託方法 傳入操做對象</param> /// <returns></returns> public static T GetCache<T>(string key, Func<T> func) { T t = default(T); if (CacheHelper.Exsits(key)) { //緩存存在,直接獲取原數據 t = CacheHelper.Get<T>(key); } else { //緩存不存在,去生成緩存,並加入容器 t = func.Invoke(); CacheHelper.Add(key, t); } return t; }
4.2 程序入口進行調用,傳入的委託參數爲lamad表達式優化後的代碼
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Cache { class Program { static void Main(string[] args) { for (int i = 1; i < 6; i++) { Console.WriteLine($"------第{i}次請求------"); int result = 0; //key的名字必定要確保請求的準確性 DataSource GetDataByDB 666缺一不可 string key = "DataSource_GetDataByDB_666"; //將須要執行的獲取數據操做編寫成委託傳入方法(重點) //Func<int> func = new Func<int>(() => { return DataSource.GetDataByDB(666); }); result = CacheHelper.GetCache(key, () => DataSource.GetDataByDB(666)); Console.WriteLine($"第{i}次請求得到的數據爲:{result}"); } } } }
到這裏,緩存的使用基本結束了。最好值得一提的是,緩存儘可能在數據量小、重複查詢量大的狀況下使用。由於緩存也是要耗內存的,服務器內存是有限的!
C#操做redis
Redis 是一個非關係型高性能的key-value數據庫。在部分場合能夠對關係數據庫起到很好的補充做用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。
redis提供五種數據類型:string,hash,list,set及zset(sorted set)。
好了,話很少說,先安裝redis吧。我這裏提供的版本是64位的3.2.1.00 https://files.cnblogs.com/files/wangjifeng23/Redis-x64-3.2.100.zip ,其他版本可前往官網進行下載 http://download.redis.io/releases/ 。
下載好以後,新建文件夾,將文件解壓。
解壓完以後,開始進行redis安裝。
1.鍵入cmd
2.指向redis安裝路徑 f: --> cd redis
3.redis安裝指令 redis-server redis.windows.conf,出現如下圖標即安裝成功
打開redis客戶端工具(redis-cli.exe)
使用set get設置獲取值,以下所示即便用成功
好了,爲了使用方便,咱們能夠把redis部署到服務上面自啓動,而後使用第三方客戶端軟件RedisDesktopManager(下載連接: https://pan.baidu.com/s/1DAWFwlZQK0AJphOQEHQaXA 密碼: jr5r)進行管理,讓開發更加便捷。
如上所示使用cmd鍵入命令: redis-server --service-install redis.windows.conf
打開客戶端,建立鏈接,輸入localhost(本機服務),鏈接前確保redis服務已開啓,端口爲6379(主服務器)
以下圖所示證實咱們已經鏈接成功啦,左邊就是我存儲的4個鍵值對數據。
好了,接下來咱們要在代碼裏實現對他的存儲以及獲取。
使用NuGet安裝ServiceStack.Redis,這是微軟提供已經封裝好的對redis操做類。包含4個dll
鏈接redis服務器,讀取以及存儲
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace redis { public partial class Login : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } public static ServiceStack.Redis.RedisClient client = new ServiceStack.Redis.RedisClient("127.0.0.1", 6379); public void login(object sender, EventArgs e) { //讀取 string name = client.Get<string>("name"); string pwd = client.Get<string>("password"); //存儲 client.Set<string>("name1", username.Value); client.Set<string>("password1", userpwd.Value); } } }
總結:
1 、Redis不只僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
2 、Redis支持數據的備份,即master-slave模式的數據備份。
3 、Redis支持數據的持久化,能夠將內存中的數據保持在磁盤中,重啓的時候能夠再次加載進行使用。
四、Redis能夠實現主從複製,實現故障恢復。
五、Redis的Sharding技術: 很容易將數據分佈到多個Redis實例中
轉載請註明出處
WPF 控件庫——可拖動選項卡的TabControl
1、先看看效果
2、原理
一、選項卡大小和位置
此次給你們介紹的控件是比較經常使用的TabControl,網上常見的TabControl樣式有不少,其中一部分也支持拖動選項卡,可是帶動畫效果的不多見。這也是有緣由的,由於想要作一個不失原有功能,還須要添加動畫效果的控件可不是一行代碼的事。要作成上圖中的效果,咱們不能一蹴而就,最忌諱的是一上來就想實現全部效果。
一開始,咱們最好先用Blend看看原生的TabControl樣式模板部分是如何實現的,這樣咱們也好有個參考。咱們先從資產面板中拖一個TabControl放到窗體中,調整好合適的大小:
而後在它上面右鍵,編輯模板->編輯副本->肯定,在自動生成的xaml代碼中關鍵部分是這裏:
能夠看到,全部的選項卡(也就是TabItem)其實都是放在TabControl內部維護的一個TabPanel中,知道這些就夠了,咱們徹底能夠作一個定製的TabPanel來替換它: public class TabPanel : Panel 。既然這個TabPanel是一個容器,因此它必須負責計算TabItem的大小還要安排它的位置,咱們能夠重載父類Panel的 MeasureOverride 方法來處理這些邏輯: protected override Size MeasureOverride(Size constraint) 。在這個方法中咱們經過 InternalChildren 這個只讀屬性來獲取選項卡,選項卡的高度咱們由 TabItemHeight 屬性指定,因爲TabPanel對用戶是透明的,因此咱們還要定製一個TabControl,裏面加上 TabItemHeight 屬性,讓它和TabPanel的綁定。以後的 TabItemWidth 和 IsEnableTabFill 也同理。而選項卡的寬度則要分狀況討論了,若是 IsEnableTabFill = true 咱們則要平分寬度,例如容器寬度爲100,選項卡有10個,那麼每一個選項卡的寬度就是10。在這裏要注意的是,選項卡的寬度最好不要有小數點,雖然有諸如 UseLayoutRounding 這種特性的幫助能夠必定程度去除模糊,但在一個個連續排列的選項卡上反而會拔苗助長,你會發現兩兩之間的分割線寬度是不一致的,最好的辦法就是「不公平的平分」,貼上一段代碼來解釋:
public static int[] DivideInt2Arr(int num, int count) { var arr = new int[count]; var div = num / count; var rest = num % count; for (int i = 0; i < count; i++) { arr[i] = div; } for (int i = 0; i < rest; i++) { arr[i] += 1; } return arr;
}
假設如今的容器寬度是108,選項卡仍是10個,經過 MeasureOverride 方法處理後,前八個的寬度則是11,後兩個是10。若是 IsEnableTabFill = false 則不要平分了,直接放入容器便可。
如今選項卡大小搞定了,位置呢?太簡單了,一個for循環不斷疊加每一個選項卡的寬度就能夠了: size.Width += tabItem.ItemWidth; 。最後經過調用 Element.Arrange 便可排布選項卡的位置:
var rect = new Rect { X = size.Width - tabItem.BorderThickness.Left, Width = itemWidth, Height = TabItemHeight }; tabItem.Arrange(rect);
由於選項卡左右都有邊距,減去一個左邊距,二者間的間隔就是一個邊距了。
選項卡大小和位置的邏輯處理大體是上述的過程,因爲篇幅有限,加之我不喜歡一貼一大段代碼,因此只挑重點來討論,完整的代碼還要考慮各類狀況,這裏就再也不贅述了。
二、動畫處理
這一部分咱們的關注點就是鼠標了,對選項卡而言,鼠標按下、鼠標移動、鼠標擡起,這些咱們都要關注,因此分別給它們訂閱一下事件。與之對應的,咱們還要給選項卡添加幾個標私有字段,用以記錄狀態,好比 _isDragging 、 _isDragged 、 _dragPoint 、 _isWaiting ,前兩個我就不說了,都是字面意思,第三個則用來暫存鼠標移動時的位置,每次進入選項卡的 OnMouseMove 事件,都要將 _isDragged 和其舊值做差,以求得當前選項卡應該移動的距離。 _isWaiting 用途比較特殊,在用戶拖動選項卡時,咱們最好等待一個粘滯距離,好比20個單位寬度,也就是說,在水平方向鼠標移動了超過20個像素無關單位後,選項卡纔開始被拖動。
在一開始的gif中能夠看到,被拖動的選項卡改變位置時,其他的選項卡也會動態改變位置,那麼位置改變的時機是如何肯定的呢?很簡單,只要將被拖動的選項卡到容器(TabPanel)左邊界的這個距離除以 ItemWidth ,結果四捨五入就是這個選項卡當前應該所處的位置,緊接着下一步就是要把這個位置上的選項卡和當前被拖動的換個位置。此刻咱們終於能夠用動畫來實現了,因爲這個系列的文章屢次講過動畫的代碼了,因此就再也不贅述。
上面一段講的是換位置,那麼添加選項卡、刪除選項卡呢?其實有個捷徑能夠走,就是使用 FluidMoveBehavior ,把他往樣式裏一塞,好了,效果出來了!
可是這裏有一個坑要注意, FluidMoveBehavior 雖然能夠化簡一部分動畫邏輯,可是它有點越權了,它把你位置移動的邏輯也給作了,你會發現,若是不加處理,在你本身的動畫結束後它還會再來一遍它的動畫。能夠將 FluidMoveBehavior 的 Duration 屬性暫時歸零來解決這個問題: FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0)); 。
這篇文章只是大體介紹一下實現的過程和思路,感興趣的能夠下載源碼,多多交流,共同提升。
3、源碼
本文所討論的控件源碼已經在github開源:https://github.com/NaBian/HandyControl
【Bootstrap系列】詳解Bootstrap-table
本篇文章將與你們分享bootstrap-table插件,藉助於它實現基本的增刪改查,導入導出,分頁,父子表等。
至於其餘技術,如凍結表頭,列排列,行拖動,列拖動等,會在後續文章中與你們分享。
一 效果圖
(一)頁面初始化
下圖是頁面首次加載結束後的效果,主要完成如下功能:
1.日期部分,開始時間:當前月第一天對應的8位日期,結束時間:當前月最後一天對應的8位日期,時間格式爲:yyyy-mm-dd
2.bootstrap-table加載的數據爲日期部分所對應的時間,且按照時間遞減展現
(二)查詢
1.支持日期查詢和訂單編號查詢
2.當日期和訂單編號都存在時,忽略日期條件(由於訂單編號是惟一的)
以下爲查詢結果:
(三)添加
1.利用dialog模態框加載AddForm頁面;
2.實現可拖拽
(四)編輯
1.利用dialog模態框加載EditForm頁面
2.根據訂單編號選擇編輯
(五)刪除
1.選中刪除
(六)導入
1.下載導入模板
2.按照模板格式導入數據
(七)導出
1.選中導出
2.導出支持多種格式
(八)父子表
1.訂單表做爲父表,產品表做爲子表
2.父表和字表經過產品編號來關聯
二 Bootstrap-table講解
關於bootstrap-table參數,須要掌握以下幾大類:表格參數,列參數,事件,方法和多語言,
詳情能夠參考bootstrap-table官網:http://bootstrap-table.wenzhixin.net.cn/zh-cn/documentation/
三 本Demo技術講解
(一)Demo架構圖
本Demo採用UI+BLL+DAL+Model三層架構。
(二)核心代碼
1.Bootstrap-table JS結構定義
![](http://static.javashuo.com/static/loading.gif)
1 //初始化 2 var InitTable = function (url) { 3 //先銷燬表格 4 $('#tb_SaleOrder').bootstrapTable("destroy"); 5 //加載表格 6 $('#tb_SaleOrder').bootstrapTable({ 7 rowStyle: function (row, index) {//row 表示行數據,object,index爲行索引,從0開始 8 var style = ""; 9 if (row.SignInTime == '' || row.SignOutTime=='') { 10 style = { css: { 'color': 'red' } }; 11 } 12 return style; 13 }, 14 //searchAlign: 'left', 15 //search: true, //顯示隱藏搜索框 16 showHeader: true, //是否顯示列頭 17 //classes: 'table-no-bordered', 18 showLoading: true, 19 undefinedText: '', 20 showFullscreen: true, 21 toolbarAlign: 'left', 22 paginationHAlign: 'right', 23 silent: true, 24 url: url, 25 method: 'get', //請求方式(*) 26 toolbar: '#toolbar', //工具按鈕用哪一個容器 27 striped: true, //是否顯示行間隔色 28 cache: false, //是否使用緩存,默認爲true,因此通常狀況下須要設置一下這個屬性(*) 29 pagination: true, //是否顯示分頁(*) 30 sortable: false, //是否啓用排序 31 sortOrder: "asc", //排序方式 32 //queryParams: InitTable.queryParams, //傳遞參數(*) 33 sidePagination: "server", //分頁方式:client客戶端分頁,server服務端分頁(*) 34 pageNumber: 1, //初始化加載第一頁,默認第一頁 35 pageSize: 10, //每頁的記錄行數(*) 36 pageList: [2, 5, 10, 15], //可供選擇的每頁的行數(*) 37 search: false, //是否顯示錶格搜索,此搜索是客戶端搜索,不會進服務端,因此,我的感受意義不大 38 strictSearch: true, 39 showColumns: true, //是否顯示全部的列 40 showRefresh: true, //是否顯示刷新按鈕 41 minimumCountColumns: 2, //最少容許的列數 42 clickToSelect: true, //是否啓用點擊選中行 43 //height: 680, //行高,若是沒有設置height屬性,表格自動根據記錄條數以爲表格高度 44 uniqueId: "ID", //每一行的惟一標識,通常爲主鍵列 45 showToggle: true, //是否顯示詳細視圖和列表視圖的切換按鈕 46 cardView: false, //是否顯示詳細視圖 47 detailView: false, //是否顯示父子表 48 showExport: true, 49 //exportDataType: 'all', 50 exportDataType: "selected", //導出checkbox選中的行數 51 paginationLoop: false, //是否無限循環 52 columns: [{ 53 checkbox: true 54 }, { 55 field: 'OrderNO', 56 title: '訂單編號' 57 }, { 58 field: 'ProductNo', 59 title: '產品編號' 60 }, { 61 field: 'CustName', 62 title: '客戶姓名' 63 }, { 64 field: 'CustAddress', 65 title: '客戶地址', 66 }, { 67 field: 'CustPhone', 68 title: '客戶電話', 69 }, { 70 field: 'CustCompany', 71 title: '客戶公司', 72 }, { 73 field: 'CreateDateTime', 74 title: '訂單建立時間', 75 }, { 76 field: 'UpdateDateTime', 77 title: '訂單更新時間', 78 }] 79 }); 80 return InitTable; 81 };
2.訂單表增刪改查
![](http://static.javashuo.com/static/loading.gif)
1 $(function () { 2 //初始時間控件 3 var frstDayDate = GetLocalMonFrstDayDate(); 4 var lastDayDate = GetLocalMonLastDayDate(); 5 $("#startDate").val(frstDayDate); 6 $("#endDate").val(lastDayDate); 7 8 //初始化bootstrap-table參數 9 var filterParam = ""; 10 var startDate = $("#startDate").val(); 11 var endDate = $("#endDate").val(); 12 url = "/SaleManage/GetOrderList?startDate=" + startDate + "&endDate=" + endDate + "&orderNO=" + filterParam + ""; 13 InitTable(url); 14 15 //查詢數據 16 $("#btn_query").click(function () { 17 var filterParam = $("#queryKey").val(); 18 var startDate = $("#startDate").val(); 19 var endDate = $("#endDate").val(); 20 var url = "/SaleManage/GetOrderList?startDate="+ startDate + "&endDate=" +endDate + "&orderNO=" + filterParam + ""; 21 InitTable(url); 22 }) 23 24 //添加 25 $("#btn_add").click(function () { 26 var url = "/SaleManage/AddForm"; 27 openDialog(url, "AddForm", "添加訂單", 645, 470, function (iframe) { 28 top.frames[iframe].AcceptClick() 29 }); 30 }) 31 32 //編輯 33 $("#btn_edit").click(function () { 34 //獲取當前選擇行id 35 var selectedRows = $("#tb_SaleOrder").bootstrapTable('getSelections'); 36 if (selectedRows.length <= 0) { 37 alert('請選擇要編輯的數據'); 38 } else if (selectedRows.length > 1) { 39 alert('一次只能選擇一行數據進行編輯'); 40 } else { 41 var KeyValue = selectedRows[0].OrderNO; 42 var url = "/SaleManage/EditForm?KeyValue=" + KeyValue; 43 openDialog(url, "EditForm", "編輯郵件", 645, 470, function (iframe) { 44 top.frames[iframe].AcceptClick() 45 }); 46 } 47 }) 48 //刪除數據 49 $("#btn_delete").click(function () { 50 //獲取當前選擇行id 51 var selectedRows = $("#tb_SaleOrder").bootstrapTable('getSelections'); 52 if (selectedRows.length <= 0) { 53 alert('請選擇要刪除的數據'); 54 return; 55 } 56 if (selectedRows.length > 1) { 57 alert('一次只能選擇一行刪除'); 58 return; 59 } 60 var orderNo = selectedRows[0].OrderNO; 61 //aja異步請求 62 $.ajax({ 63 url: '/SaleManage/DelOrder', 64 type: 'get', 65 contentType: 'application/json;charset=utf-8', 66 data: { orderNo: orderNo }, 67 success: function (data) { 68 //刷新bootstrap-table 69 $("#tb_SaleOrder").bootstrapTable('refresh'); 70 }, 71 error: function (data) { 72 alert('數據刪除失敗' + data); 73 } 74 }) 75 }) 76 77 //回車鍵 78 document.onkeydown = function (e) { 79 if (!e) e = window.event; //火狐中是 window.event 80 if ((e.keyCode || e.which) == 13) { 81 var query = document.getElementById("btn_query"); 82 query.focus(); 83 query.click(); 84 } 85 } 86 });
3.日期初始化
![](http://static.javashuo.com/static/loading.gif)
1 //當月第一天所對應的日期 yyyy-mm-dd 2 function GetLocalMonFrstDayDate() { 3 var now = new Date(); 4 var year = now.getFullYear();//年 5 var mon = now.getMonth() + 1;//月 6 if (mon < 10) { 7 mon = '-0' + mon; 8 } 9 var frstDay = "-01"; //日 10 return year + mon + frstDay; 11 } 12 //當月最後一天所對應的日期 yyyy-mm-dd 13 function GetLocalMonLastDayDate() { 14 var now = new Date(); 15 var year = now.getFullYear();//年 16 var mon = now.getMonth() + 1;//月 17 if (mon < 10) { 18 mon = '-0' + mon; 19 } 20 var LastDay = "-" + GetDayCountInMon(year + mon); 21 return year + mon + LastDay; 22 } 23 //計算當月對應的最大天數 24 function GetDayCountInMon(YearMon) { 25 var arr = YearMon.split("-"); 26 var localYear = parseInt(arr[0]); 27 var localMon = parseInt(arr[1]); 28 var localDate = new Date(localYear, localMon, 0); 29 return localDate.getDate(); 30 }
4.Index.cshtml
![](http://static.javashuo.com/static/loading.gif)
1 @{ 2 Layout = "~/Views/Shared/_LayoutBTSTable.cshtml"; 3 } 4 5 <!--查詢條件--> 6 <div class="panel-body" style="padding-bottom:0px;width:104%;margin-left:-15px"> 7 <div class="panel panel-default"> 8 <div class="panel-heading"> 9 訂單管理 10 </div> 11 <div style="margin-top:-30px;text-align:right"> 12 <a href="~/Files/ImportTemple.xlsx" style="margin-right:20px">下載導入模板 </a> 13 </div> 14 <div class="panel-body"> 15 <div style="margin-top:10px;"> 16 日期: 17 <input type="text" id="startDate" style="height:35px;width:100px;margin-left:5px;margin-top:-32px;border-radius: 6px;border: 1px #cccccc solid; outline: none" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd'})"> 18 — 19 <input type="text" id="endDate" style="height:35px;width:100px;margin-left:8px;margin-top:-34px;border-radius: 6px;border: 1px #cccccc solid; outline: none" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd'})"> 20 訂單編號:<input type="text" id="queryKey" placeholder="請輸入訂單編號進行查詢" style="height:35px;width:170px;margin-left:10px;margin-top:-34px;border-radius: 6px;border: 1px #cccccc solid; outline: none"> 21 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_query" class="btn btn-success">查詢</button> 22 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_add" class="btn btn-info">添加</button> 23 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_edit" class="btn btn-warning">編輯</button> 24 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_delete" class="btn btn-danger">刪除</button> 25 </div> 26 </div> 27 </div> 28 </div> 29 <!--初始化bootstrap-table--> 30 <div style="margin-bottom:-40px;color:red">註釋:訂單數據</div> 31 <table id="tb_SaleOrder" class="table"></table> 32 33 <style> 34 #tb_SaleOrder tbody > tr:hover { 35 background-color: #449d44; 36 } 37 38 #tb_SaleOrder > thead th { 39 padding: 0; 40 margin: 0; 41 background-color: #d9edf7; 42 } 43 </style> 44 <script> 45 //刷新bootstrap-table 46 function refleshBootStrapTable() { 47 $("#tb_SaleOrder").bootstrapTable('refresh'); 48 } 49 </script> 50 51 <script src="~/CustomUI/TableJS/SaleOrder.js"></script>
5.AddForm.cshtml
![](http://static.javashuo.com/static/loading.gif)
1 @{ 2 ViewBag.Title = "AddForm"; 3 Layout = "~/Views/Shared/_LayoutBTSTable.cshtml"; 4 } 5 6 <script> 7 //添加數據 8 function AcceptClick() { 9 var OrderNO = $("#OrderNO").val(); 10 var ProductNo = $("#ProductNo").val(); 11 var CustName = $("#CustName").val(); 12 var CustAddress = $("#CustAddress").val(); 13 var CustPhone = $("#CustPhone").val(); 14 var CustCompany = $("#CustCompany").val(); 15 var CreateDateTime = $("#CreateDateTime").val(); 16 var UpdateDateTime = $("#UpdateDateTime").val(); 17 $.ajax({ 18 url: '/SaleManage/AddDataToDB', 19 type: 'get', 20 contentType: 'application/json;charset=utf-8', 21 data: { 22 'OrderNO': OrderNO, 'ProductNo': ProductNo, 'CustName': CustName, 23 'CustAddress': CustAddress, 'CustPhone': CustPhone, 'CustCompany': CustCompany, 24 'CreateDateTime': CreateDateTime, 'UpdateDateTime': UpdateDateTime 25 }, 26 success: function (data) { 27 reflesh(); 28 //關閉對話框 29 closeDialog(); 30 }, 31 error: function (data) { 32 alert('添加數據失敗' + data); 33 } 34 }) 35 } 36 //刷新 37 function reflesh() { 38 window.parent.refleshBootStrapTable(); 39 } 40 </script> 41 42 43 <div class="table" style="width:100%;margin-top:10px"> 44 <table id="tb_SaleOrder_Add" class="table text-nowrap" style="text-align:right;"> 45 <tr> 46 <td style="height:35px;line-height:35px">訂單編號 :</td> 47 <td><input type="text" id="OrderNO" style="width:500px;" /></td> 48 </tr> 49 <tr> 50 <td style="height:35px;line-height:35px">產品名稱 :</td> 51 <td><input type="text" id="ProductNo" style="width:500px;" /></td> 52 </tr> 53 <tr> 54 <td style="height:35px;line-height:35px">客戶姓名 :</td> 55 <td><input type="text" id="CustName" style="width:500px;" /></td> 56 </tr> 57 <tr> 58 <td style="height:35px;line-height:35px">客戶地址 :</td> 59 <td><input type="text" id="CustAddress" style="width:500px;" /></td> 60 </tr> 61 <tr> 62 <td style="height:35px;line-height:35px">客戶電話 :</td> 63 <td><input type="text" id="CustPhone" style="width:500px;" /></td> 64 </tr> 65 <tr> 66 <td style="height:35px;line-height:35px">客戶公司 :</td> 67 <td><input type="text" id="CustCompany" style="width:500px;" /></td> 68 </tr> 69 <tr> 70 <td style="height:35px;line-height:35px">訂單建立時間 :</td> 71 <td><input type="text" id="CreateDateTime" style="width:500px;" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" class="Wdate"/></td> 72 </tr> 73 <tr> 74 <td style="height:35px;line-height:35px">訂單更新時間 :</td> 75 <td><input type="text" id="UpdateDateTime" style="width:500px;" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" class="Wdate"/></td> 76 </tr> 77 </table> 78 </div> 79 80 <style> 81 #tb_SaleOrder_Add td input[type=text] { 82 height: 35px; 83 border-radius: 6px; 84 border: 1px #cccccc solid; 85 outline: none 86 } 87 </style>
6.EditForm.cshtml
![](http://static.javashuo.com/static/loading.gif)
@{ ViewBag.Title = "EditForm"; Layout = "~/Views/Shared/_LayoutBTSTable.cshtml"; } <script> $(function () { //初始化頁面控件 $.ajax({ url: "/SaleManage/InitModifySheet", type: 'get', contentType: 'application/json;charset=utf-8', data: { orderNO: GetQuery('KeyValue') }, success: function (data) { //將回調數據轉化爲json對象 var jsonData = eval(data); //遍歷,爲表單賦值 $("#OrderNO").val(jsonData[0].OrderNO); $("#ProductNo").val(jsonData[0].ProductNo); $("#CustName").val(jsonData[0].CustName); $("#CustAddress").val(jsonData[0].CustAddress); $("#CustPhone").val(jsonData[0].CustPhone); $("#CustCompany").val(jsonData[0].CustCompany); $("#CreateDateTime").val(jsonData[0].CreateDateTime); $("#UpdateDateTime").val(jsonData[0].UpdateDateTime); }, error: function (data) { alert('編輯數據失敗' + data); } }) }) //添加數據 function AcceptClick() { var OrderNO = $("#OrderNO").val(); var ProductNo = $("#ProductNo").val(); var CustName = $("#CustName").val(); var CustAddress = $("#CustAddress").val(); var CustPhone = $("#CustPhone").val(); var CustCompany = $("#CustCompany").val(); var CreateDateTime = $("#CreateDateTime").val(); var UpdateDateTime = $("#UpdateDateTime").val(); $.ajax({ url: '/SaleManage/ModifyDataToDB', type: 'get', contentType: 'application/json;charset=utf-8', data: { 'OrderNO': OrderNO, 'ProductNo': ProductNo, 'CustName': CustName, 'CustAddress': CustAddress, 'CustPhone': CustPhone, 'CustCompany': CustCompany, 'CreateDateTime': CreateDateTime, 'UpdateDateTime': UpdateDateTime }, success: function (data) { reflesh(); //關閉對話框 closeDialog(); }, error: function (data) { alert('添加數據失敗' + data); } }) } //刷新 function reflesh() { window.parent.refleshBootStrapTable(); } </script> <div class="table" style="width:100%;margin-top:10px"> <table id="tb_SaleOrder_Add" class="table text-nowrap" style="text-align:right;"> <tr> <td style="height:35px;line-height:35px">訂單編號 :</td> <td><input type="text" id="OrderNO" style="width:500px;" disabled/></td> </tr> <tr> <td style="height:35px;line-height:35px">產品名稱 :</td> <td><input type="text" id="ProductNo" style="width:500px;" /></td> </tr> <tr> <td style="height:35px;line-height:35px">客戶姓名 :</td> <td><input type="text" id="CustName" style="width:500px;" /></td> </tr> <tr> <td style="height:35px;line-height:35px">客戶地址 :</td> <td><input type="text" id="CustAddress" style="width:500px;" /></td> </tr> <tr> <td style="height:35px;line-height:35px">客戶電話 :</td> <td><input type="text" id="CustPhone" style="width:500px;" /></td> </tr> <tr> <td style="height:35px;line-height:35px">客戶公司 :</td> <td><input type="text" id="CustCompany" style="width:500px;" /></td> </tr> <tr> <td style="height:35px;line-height:35px">訂單建立時間 :</td> <td><input type="text" id="CreateDateTime" style="width:500px;" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" class="Wdate" /></td> </tr> <tr> <td style="height:35px;line-height:35px">訂單更新時間 :</td> <td><input type="text" id="UpdateDateTime" style="width:500px;" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" class="Wdate" /></td> </tr> </table> </div> <style> #tb_SaleOrder_Add td input[type=text] { height: 35px; border-radius: 6px; border: 1px #cccccc solid; outline: none } </style>
7.Import.cshtml
![](http://static.javashuo.com/static/loading.gif)
1 @{ 2 ViewBag.Title = "EditForm"; 3 Layout = "~/Views/Shared/_LayoutBTSTable.cshtml"; 4 } 5 6 <script> 7 $(function () { 8 //初始化頁面控件 9 $.ajax({ 10 url: "/SaleManage/InitModifySheet", 11 type: 'get', 12 contentType: 'application/json;charset=utf-8', 13 data: { 14 orderNO: GetQuery('KeyValue') 15 }, 16 success: function (data) { 17 //將回調數據轉化爲json對象 18 var jsonData = eval(data); 19 //遍歷,爲表單賦值 20 $("#OrderNO").val(jsonData[0].OrderNO); 21 $("#ProductNo").val(jsonData[0].ProductNo); 22 $("#CustName").val(jsonData[0].CustName); 23 $("#CustAddress").val(jsonData[0].CustAddress); 24 $("#CustPhone").val(jsonData[0].CustPhone); 25 $("#CustCompany").val(jsonData[0].CustCompany); 26 $("#CreateDateTime").val(jsonData[0].CreateDateTime); 27 $("#UpdateDateTime").val(jsonData[0].UpdateDateTime); 28 }, 29 error: function (data) { 30 alert('編輯數據失敗' + data); 31 } 32 }) 33 }) 34 35 //添加數據 36 function AcceptClick() { 37 var OrderNO = $("#OrderNO").val(); 38 var ProductNo = $("#ProductNo").val(); 39 var CustName = $("#CustName").val(); 40 var CustAddress = $("#CustAddress").val(); 41 var CustPhone = $("#CustPhone").val(); 42 var CustCompany = $("#CustCompany").val(); 43 var CreateDateTime = $("#CreateDateTime").val(); 44 var UpdateDateTime = $("#UpdateDateTime").val(); 45 $.ajax({ 46 url: '/SaleManage/ModifyDataToDB', 47 type: 'get', 48 contentType: 'application/json;charset=utf-8', 49 data: { 50 'OrderNO': OrderNO, 'ProductNo': ProductNo, 'CustName': CustName, 51 'CustAddress': CustAddress, 'CustPhone': CustPhone, 'CustCompany': CustCompany, 52 'CreateDateTime': CreateDateTime, 'UpdateDateTime': UpdateDateTime 53 }, 54 success: function (data) { 55 reflesh(); 56 //關閉對話框 57 closeDialog(); 58 }, 59 error: function (data) { 60 alert('添加數據失敗' + data); 61 } 62 }) 63 } 64 //刷新 65 function reflesh() { 66 window.parent.refleshBootStrapTable(); 67 } 68 </script> 69 70 71 72 <div class="table" style="width:100%;margin-top:10px"> 73 <table id="tb_SaleOrder_Add" class="table text-nowrap" style="text-align:right;"> 74 <tr> 75 <td style="height:35px;line-height:35px">訂單編號 :</td> 76 <td><input type="text" id="OrderNO" style="width:500px;" disabled/></td> 77 </tr> 78 <tr> 79 <td style="height:35px;line-height:35px">產品名稱 :</td> 80 <td><input type="text" id="ProductNo" style="width:500px;" /></td> 81 </tr> 82 <tr> 83 <td style="height:35px;line-height:35px">客戶姓名 :</td> 84 <td><input type="text" id="CustName" style="width:500px;" /></td> 85 </tr> 86 <tr> 87 <td style="height:35px;line-height:35px">客戶地址 :</td> 88 <td><input type="text" id="CustAddress" style="width:500px;" /></td> 89 </tr> 90 <tr> 91 <td style="height:35px;line-height:35px">客戶電話 :</td> 92 <td><input type="text" id="CustPhone" style="width:500px;" /></td> 93 </tr> 94 <tr> 95 <td style="height:35px;line-height:35px">客戶公司 :</td> 96 <td><input type="text" id="CustCompany" style="width:500px;" /></td> 97 </tr> 98 <tr> 99 <td style="height:35px;line-height:35px">訂單建立時間 :</td> 100 <td><input type="text" id="CreateDateTime" style="width:500px;" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" class="Wdate" /></td> 101 </tr> 102 <tr> 103 <td style="height:35px;line-height:35px">訂單更新時間 :</td> 104 <td><input type="text" id="UpdateDateTime" style="width:500px;" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" class="Wdate" /></td> 105 </tr> 106 </table> 107 </div> 108 109 <style> 110 #tb_SaleOrder_Add td input[type=text] { 111 height: 35px; 112 border-radius: 6px; 113 border: 1px #cccccc solid; 114 outline: none 115 } 116 </style>
8.ParentAndChild.cshtml
![](http://static.javashuo.com/static/loading.gif)
1 @{ 2 Layout = "~/Views/Shared/_LayoutBTSTable.cshtml"; 3 } 4 5 <!--查詢條件--> 6 <div class="panel-body" style="padding-bottom:0px;width:104%;margin-left:-15px"> 7 <div class="panel panel-default"> 8 <div class="panel-heading"> 9 訂單管理 10 </div> 11 <div style="margin-top:-30px;text-align:right"> 12 <a href="~/Files/ImportTemple.xlsx" style="margin-right:20px">下載導入模板 </a> 13 </div> 14 <div class="panel-body"> 15 <div style="margin-top:10px;"> 16 日期: 17 <input type="text" id="startDate" style="height:35px;width:100px;margin-left:5px;margin-top:-32px;border-radius: 6px;border: 1px #cccccc solid; outline: none" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd'})"> 18 — 19 <input type="text" id="endDate" style="height:35px;width:100px;margin-left:8px;margin-top:-34px;border-radius: 6px;border: 1px #cccccc solid; outline: none" onfocus="WdatePicker({dateFmt:'yyyy-MM-dd'})"> 20 訂單編號:<input type="text" id="queryKey" placeholder="請輸入訂單編號進行查詢" style="height:35px;width:170px;margin-left:10px;margin-top:-34px;border-radius: 6px;border: 1px #cccccc solid; outline: none"> 21 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_query" class="btn btn-success">查詢</button> 22 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_add" class="btn btn-info">添加</button> 23 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_edit" class="btn btn-warning">編輯</button> 24 <button type="button" style="width:70px;height:35px;margin-left:20px;margin-top:-3px" id="btn_delete" class="btn btn-danger">刪除</button> 25 </div> 26 </div> 27 </div> 28 </div> 29 <!--初始化bootstrap-table--> 30 <div style="margin-bottom:-40px;color:red">註釋:父子表</div> 31 <table id="tb_SaleOrder" class="table"></table> 32 33 <style> 34 #tb_SaleOrder > thead th { 35 padding: 0; 36 margin: 0; 37 background-color: #d9edf7; 38 } 39 </style> 40 <script> 41 //刷新bootstrap-table 42 function refleshBootStrapTable() { 43 $("#tb_SaleOrder").bootstrapTable('refresh'); 44 } 45 </script> 46 47 <script src="~/CustomUI/TableJS/ParentChild.js"></script>
9.佈局頁 _LayoutBTSTable.cshtml
![](http://static.javashuo.com/static/loading.gif)
1 <!DOCTYPE html> 2 3 <html> 4 <head> 5 <meta name="viewport" content="width=device-width" /> 6 <link href="~/CustomUI/bootstrap/css/bootstrap.css" rel="stylesheet" /> 7 <link href="~/CustomUI/bootstrapTable/bootstrap-table.css" rel="stylesheet" /> 8 <link href="~/CustomUI/skin/WdatePicker.css" rel="stylesheet" /> 9 <script src="~/CustomUI/jquery/jquery-3.3.1.js"></script> 10 <script src="~/CustomUI/lhgdialog/lhgdialog.min.js"></script> 11 <script src="~/CustomUI/bootstrap/js/bootstrap.js"></script> 12 <script src="~/CustomUI/bootstrapTable/bootstrap-table.js"></script> 13 <script src="~/CustomUI/bootstrapTable/tableExport.js"></script> 14 <script src="~/CustomUI/bootstrapTable/bootstrap-table-export.js"></script> 15 <script src="~/CustomUI/bootstrapTable/bootstrap-table-zh-CN.js"></script> 16 <script src="~/CustomUI/datepicker/WdatePicker.js"></script> 17 </head> 18 <body> 19 <div> 20 @RenderBody() 21 </div> 22 </body> 23 </html> 24 25 <script src="~/CustomUI/TableJS/DialogTemple.js"></script>
10.ImportExcelToDB.cs
![](http://static.javashuo.com/static/loading.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Configuration; 4 using System.Data; 5 using System.Data.OleDb; 6 using System.Data.SqlClient; 7 using System.Linq; 8 using System.Text; 9 using System.Web; 10 11 namespace BTStrapTB.Common 12 { 13 public class ImportExcelToDB 14 { 15 //全局數據庫鏈接字符串 16 private readonly string strConnection = ConfigurationManager.ConnectionStrings["conStr"].ConnectionString; 17 18 //從Excel讀取數據 19 public static DataSet ReadExcelDataToTable(string filepath) 20 { 21 try 22 { 23 int index1 = filepath.LastIndexOf("\\"); 24 int index2 = filepath.LastIndexOf("."); 25 string fileName ="["+filepath.Substring(index1+1,index2-index1-1)+"$]"; 26 string strConn = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'", filepath); 27 using (OleDbConnection oleConn = new OleDbConnection(strConn)) 28 { 29 oleConn.Open(); 30 string sql = "select * from "+fileName+ ""; 31 OleDbDataAdapter oleDaExcel = new OleDbDataAdapter(sql, oleConn); 32 DataSet oleDsExcel = new DataSet(); 33 oleDaExcel.Fill(oleDsExcel, "table1"); 34 return oleDsExcel; 35 } 36 } 37 catch (Exception ex) 38 { 39 throw ex; 40 } 41 } 42 public void InsertExcelDataToDB(string fileName) 43 { 44 //導入表格格式化SQL 45 string sqlText = @"INSERT INTO [dbo].[SaleOrder] 46 ([OrderNO] 47 ,[ProductNo] 48 ,[CustName] 49 ,[CustAddress] 50 ,[CustPhone] 51 ,[CustCompany] 52 ,[CreateDateTime] 53 ,[UpDateDateTime]) 54 VALUES 55 ('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}')"; 56 57 if (!System.IO.File.Exists(fileName)) 58 { 59 throw new Exception("指定路徑的Excel文件不存在!"); 60 } 61 DataSet ds = ReadExcelDataToTable(fileName); 62 DataTable dt = ds.Tables[0]; 63 //將excel數據插入到DB以前,先判斷DB中是否存在數據 64 DelDBRepeatData(dt); 65 List<string> list = (from DataRow row in dt.Rows 66 select String.Format(sqlText, row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])).ToList(); 67 OperateDB(list); 68 } 69 70 /* 71 將excel數據插入到DB以前, 72 先判斷DB中是否存在同一天同一員工記錄 73 */ 74 public int DelDBRepeatData(DataTable dt) 75 { 76 //sql腳本 77 string delSqlText = @"DELETE FROM [dbo].[SaleOrder] 78 WHERE OrderNO IN ('{0}') 79 "; 80 81 //取excel中的員工號和打卡日期 82 StringBuilder strBld = new StringBuilder(); 83 84 for (int i = 0; i < dt.Rows.Count; i++) 85 { 86 strBld.Append(dt.Rows[i][0]); 87 88 } 89 90 List<string> list = (from DataRow row in dt.Rows 91 select String.Format(delSqlText, row[0])).ToList(); 92 93 OperateDB(list); 94 return 0; 95 } 96 97 //DB操做 98 public int OperateDB(List<string> list) 99 { 100 int result = 0; 101 using (SqlConnection conn = new SqlConnection(strConnection)) 102 { 103 if (conn.State==ConnectionState.Closed) 104 { 105 conn.Open(); 106 } 107 foreach (string item in list) 108 { 109 SqlCommand cmd = new SqlCommand(item, conn); 110 result=cmd.ExecuteNonQuery(); 111 } 112 } 113 return result; 114 } 115 } 116 }
12.ConvertHelpers.cs
![](http://static.javashuo.com/static/loading.gif)
1 using Newtonsoft.Json; 2 using System; 3 using System.Collections.Generic; 4 using System.Data; 5 using System.Linq; 6 using System.Reflection; 7 using System.Web; 8 9 namespace BTStrapTB.Common 10 { 11 /// <summary> 12 /// 轉換Json格式幫助類 13 /// </summary> 14 public static class JsonHelper 15 { 16 public static object ToJson(this string Json) 17 { 18 return JsonConvert.DeserializeObject(Json); 19 } 20 public static string ToJson(this object obj) 21 { 22 return JsonConvert.SerializeObject(obj); 23 } 24 public static List<T> JonsToList<T>(this string Json) 25 { 26 return JsonConvert.DeserializeObject<List<T>>(Json); 27 } 28 public static T JsonToEntity<T>(this string Json) 29 { 30 return JsonConvert.DeserializeObject<T>(Json); 31 } 32 } 33 }
13.SaleManageController
![](http://static.javashuo.com/static/loading.gif)
1 using BTStrapTB.BLL; 2 using BTStrapTB.Common; 3 using BTStrapTB.Models; 4 using System; 5 using System.Collections.Generic; 6 using System.IO; 7 using System.Linq; 8 using System.Web; 9 using System.Web.Mvc; 10 11 namespace BTStrapTB.Controllers 12 { 13 //銷售管理 14 public class SaleManageController : BaseManageController 15 { 16 ImportExcelToDB ImportToExcl = new ImportExcelToDB(); 17 SaleOrderBLL SOBLL = new SaleOrderBLL(); 18 SaleProductBLL SPBLL = new SaleProductBLL(); 19 public override ActionResult Index() 20 { 21 return View(); 22 } 23 //導入頁面 24 public ActionResult Import() 25 { 26 return View(); 27 } 28 29 //將Excel訂單數據導入 30 [HttpPost] 31 public ActionResult ImportExclToDB(HttpPostedFileBase file) 32 { 33 var severPath = this.Server.MapPath("/Files"); //獲取當前虛擬文件路徑 34 var savePath = Path.Combine(severPath, file.FileName); //拼接保存文件路徑 35 file.SaveAs(savePath); 36 try 37 { 38 ImportToExcl.InsertExcelDataToDB(savePath); 39 return Content("<script>alert('上傳成功!!')</script>"); 40 } 41 catch (Exception ex) 42 { 43 throw new Exception(ex.Message); 44 } 45 46 //Response.Redirect("/PunchCardRecord/Index"); 47 } 48 49 //父子頁面 50 public ActionResult ParentAndChild() 51 { 52 return View(); 53 } 54 55 //獲取子表數據 56 public ActionResult GetChildDataList(int limit, int offset,string productNo) 57 { 58 List<SaleProduct> list = SPBLL.GetProductOrderList(productNo); 59 int total = list.Count; 60 var rows = list.Skip(offset).Take(limit).ToList(); 61 return Json(new { total, rows }, JsonRequestBehavior.AllowGet); 62 } 63 64 //獲取訂單列表 65 public ActionResult GetOrderList(int limit, int offset,string startDate,string endDate,string orderNO) 66 { 67 List<SaleOrder> list = SOBLL.GetSaleOrderList(startDate,endDate, orderNO); 68 int total = list.Count; 69 var rows = list.Skip(offset).Take(limit).ToList(); 70 return Json(new { total, rows }, JsonRequestBehavior.AllowGet); 71 } 72 //刪除數據 73 public void DelOrder(string orderNo) 74 { 75 SOBLL.DelDataToDB(orderNo); 76 } 77 //添加數據 78 public void AddDataToDB(SaleOrder saleOrder) 79 { 80 SOBLL.AddDataToDB(saleOrder); 81 } 82 //初始化修改表單 83 public ActionResult InitModifySheet(string orderNO) 84 { 85 List<SaleOrder> list = SOBLL.GetSaleOrderList("", "", orderNO); 86 return Content(list.ToJson()); 87 } 88 //修改數據 89 public void ModifyDataToDB(SaleOrder saleOrder) 90 { 91 SOBLL.ModifyDataToDB(saleOrder); 92 } 93 } 94 }
14.父子表JS
![](http://static.javashuo.com/static/loading.gif)
1 //初始化 2 var InitTable = function (url) { 3 //先銷燬表格 4 $('#tb_SaleOrder').bootstrapTable("destroy"); 5 //加載表格 6 $('#tb_SaleOrder').bootstrapTable({ 7 rowStyle: function (row, index) {//row 表示行數據,object,index爲行索引,從0開始 8 var style = ""; 9 if (row.SignInTime == '' || row.SignOutTime == '') { 10 style = { css: { 'color': 'red' } }; 11 } 12 return style; 13 }, 14 //searchAlign: 'left', 15 //search: true, //顯示隱藏搜索框 16 showHeader: true, //是否顯示列頭 17 //classes: 'table-no-bordered', 18 showLoading: true, 19 undefinedText: '', 20 showFullscreen: true, 21 toolbarAlign: 'left', 22 paginationHAlign: 'right', 23 silent: true, 24 url: url, 25 method: 'get', //請求方式(*) 26 toolbar: '#toolbar', //工具按鈕用哪一個容器 27 striped: true, //是否顯示行間隔色 28 cache: false, //是否使用緩存,默認爲true,因此通常狀況下須要設置一下這個屬性(*) 29 pagination: true, //是否顯示分頁(*) 30 sortable: false, //是否啓用排序 31 sortOrder: "asc", //排序方式 32 //queryParams: InitTable.queryParams, //傳遞參數(*) 33 sidePagination: "server", //分頁方式:client客戶端分頁,server服務端分頁(*) 34 pageNumber: 1, //初始化加載第一頁,默認第一頁 35 pageSize: 10, //每頁的記錄行數(*) 36 pageList: [2, 5, 10, 15], //可供選擇的每頁的行數(*) 37 search: false, //是否顯示錶格搜索,此搜索是客戶端搜索,不會進服務端,因此,我的感受意義不大 38 strictSearch: true, 39 showColumns: true, //是否顯示全部的列 40 showRefresh: true, //是否顯示刷新按鈕 41 minimumCountColumns: 2, //最少容許的列數 42 clickToSelect: true, //是否啓用點擊選中行 43 //height: 680, //行高,若是沒有設置height屬性,表格自動根據記錄條數以爲表格高度 44 uniqueId: "ID", //每一行的惟一標識,通常爲主鍵列 45 showToggle: true, //是否顯示詳細視圖和列表視圖的切換按鈕 46 cardView: false, //是否顯示詳細視圖 47 detailView: true, //是否顯示父子表 48 showExport: true, 49 //exportDataType: 'all', 50 exportDataType: "selected", //導出checkbox選中的行數 51 paginationLoop: false, //是否無限循環 52 columns: [{ 53 checkbox: true 54 }, { 55 field: 'OrderNO', 56 title: '訂單編號' 57 }, { 58 field: 'ProductNo', 59 title: '產品編號' 60 }, { 61 field: 'CustName', 62 title: '客戶姓名' 63 }, { 64 field: 'CustAddress', 65 title: '客戶地址', 66 }, { 67 field: 'CustPhone', 68 title: '客戶電話', 69 }, { 70 field: 'CustCompany', 71 title: '客戶公司', 72 }, { 73 field: 'CreateDateTime', 74 title: '訂單建立時間', 75 }, { 76 field: 'UpdateDateTime', 77 title: '訂單更新時間', 78 }], 79 80 //無限循環取子表,直到子表裏面沒有記錄 81 onExpandRow: function (index, row, $Subdetail) { 82 InitSubTable(index, row, $Subdetail); 83 } 84 }); 85 return InitTable; 86 87 88 }; 89 90 //初始化子表格(無線循環) 91 InitSubTable = function (index, row, $detail) { 92 var parentid = row.ProductNo; 93 var cur_table = $detail.html('<table></table>').find('table'); 94 $(cur_table).bootstrapTable({ 95 url: "/SaleManage/GetChildDataList", 96 method: 'get', 97 queryParams: { 'limit':10000,'offset':0,'productNo':parentid}, 98 clickToSelect: true, 99 detailView: false,//父子表 100 sidePagination: "server", 101 uniqueId: "ProductNo", 102 pageSize: 10, 103 pageList: [10, 25], 104 columns: [{ 105 field: 'ProductNo', 106 title: '產品編號' 107 }, 108 { 109 field: 'ProductName', 110 title: '產品名稱' 111 }, { 112 field: 'ProductType', 113 title: '產品類型' 114 }, { 115 field: 'ProductCount', 116 title: '產品數量' 117 }, 118 { 119 field: 'ProductPrice', 120 title: '產品單價' 121 }], 122 //無限循環取子表,直到子表裏面沒有記錄 123 onExpandRow: function (index, row, $Subdetail) { 124 InitSubTable(index, row, $Subdetail); 125 } 126 }); 127 };
(三)其餘技術點
1.改變bootstrap-table表頭顏色
1 #tb_SaleOrder > thead th { 2 padding: 0; 3 margin: 0; 4 background-color: #d9edf7; 5 }
2.改變bootstrap-table 光標懸停顏色
1 #tb_SaleOrder tbody > tr:hover { 2 background-color: #449d44; 3 }
3.刷新bootstrap-table
1 //刷新bootstrap-table 2 function refleshBootStrapTable() { 3 $("#tb_SaleOrder").bootstrapTable('refresh'); 4 }
4.彈窗
1 /* 2 彈出對話框(帶:確認按鈕、取消按鈕) 3 */ 4 function openDialog(url, _id, _title, _width, _height, callBack) { 5 Loading(true); 6 top.$.dialog({ 7 id: _id, 8 width: _width, 9 height: _height, 10 max: false, 11 lock: true, 12 title: _title, 13 resize: false, 14 extendDrag: true, 15 content: 'url:' + RootPath() + url, 16 ok: function () { 17 callBack(_id); 18 return false; 19 }, 20 cancel: true 21 }); 22 }
5.Bootstrap-table核心技術點,再次強調
1 var InitTable = function (url) { 2 //先銷燬表格 3 $('#tb_SaleOrder').bootstrapTable("destroy"); 4 //加載表格 5 $('#tb_SaleOrder').bootstrapTable({ 6 rowStyle: function (row, index) {//row 表示行數據,object,index爲行索引,從0開始 7 var style = ""; 8 if (row.SignInTime == '' || row.SignOutTime=='') { 9 style = { css: { 'color': 'red' } }; 10 } 11 return style; 12 }, 13 //searchAlign: 'left', 14 //search: true, //顯示隱藏搜索框 15 showHeader: true, //是否顯示列頭 16 //classes: 'table-no-bordered', 17 showLoading: true, 18 undefinedText: '', 19 showFullscreen: true, 20 toolbarAlign: 'left', 21 paginationHAlign: 'right', 22 silent: true, 23 url: url, 24 method: 'get', //請求方式(*) 25 toolbar: '#toolbar', //工具按鈕用哪一個容器 26 striped: true, //是否顯示行間隔色 27 cache: false, //是否使用緩存,默認爲true,因此通常狀況下須要設置一下這個屬性(*) 28 pagination: true, //是否顯示分頁(*) 29 sortable: false, //是否啓用排序 30 sortOrder: "asc", //排序方式 31 //queryParams: InitTable.queryParams, //傳遞參數(*) 32 sidePagination: "server", //分頁方式:client客戶端分頁,server服務端分頁(*) 33 pageNumber: 1, //初始化加載第一頁,默認第一頁 34 pageSize: 10, //每頁的記錄行數(*) 35 pageList: [2, 5, 10, 15], //可供選擇的每頁的行數(*) 36 search: false, //是否顯示錶格搜索,此搜索是客戶端搜索,不會進服務端,因此,我的感受意義不大 37 strictSearch: true, 38 showColumns: true, //是否顯示全部的列 39 showRefresh: true, //是否顯示刷新按鈕 40 minimumCountColumns: 2, //最少容許的列數 41 clickToSelect: true, //是否啓用點擊選中行 42 //height: 680, //行高,若是沒有設置height屬性,表格自動根據記錄條數以爲表格高度 43 uniqueId: "ID", //每一行的惟一標識,通常爲主鍵列 44 showToggle: true, //是否顯示詳細視圖和列表視圖的切換按鈕 45 cardView: false, //是否顯示詳細視圖 46 detailView: false, //是否顯示父子表 47 showExport: true, 48 //exportDataType: 'all', 49 exportDataType: "selected", //導出checkbox選中的行數 50 paginationLoop: false, //是否無限循環 51 columns: [{ 52 checkbox: true 53 }, { 54 field: 'OrderNO', 55 title: '訂單編號' 56 }, { 57 field: 'ProductNo', 58 title: '產品編號' 59 }, { 60 field: 'CustName', 61 title: '客戶姓名' 62 }, { 63 field: 'CustAddress', 64 title: '客戶地址', 65 }, { 66 field: 'CustPhone', 67 title: '客戶電話', 68 }, { 69 field: 'CustCompany', 70 title: '客戶公司', 71 }, { 72 field: 'CreateDateTime', 73 title: '訂單建立時間', 74 }, { 75 field: 'UpdateDateTime', 76 title: '訂單更新時間', 77 }] 78 }); 79 return InitTable; 80 };
四 寫在最後
本片文章藉助於bootstrap-table插件,實現了基本的增刪改查,導入導出,分頁,父子表等。至於其餘技術,如凍結表頭,列排列,行拖動,列拖動等,會在後續文章中與你們分享。
AutoFac
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace FB.CMS.MvcSite.App_Start { using Autofac; using Autofac.Integration.Mvc; using System.Reflection; using System.Web.Mvc; /// <summary> /// 這個類是我本身定義的一個類,主要用初始化AutoFac容器的相關數據 /// </summary> public class AutoFacConfig { public static void Register() { //初始化AutoFac的相關功能 /* 1.0 告訴AutoFac初始化數據倉儲層FB.CMS.Repository.dll中全部類的對象實例。這些對象實例以其接口的形式保存在AutoFac容器中 2.0 告訴AutoFac初始化業務邏輯層FB.CMS.Services.dll中全部類的對象實例。這些對象實例以其接口的形式保存在AutoFac容器中 3.0 將MVC默認的控制器工廠替換成AutoFac的工廠 */ //第一步: 構造一個AutoFac的builder容器 ContainerBuilder builder = new Autofac.ContainerBuilder(); //第二步:告訴AutoFac控制器工廠,控制器類的建立去哪些程序集中查找(默認控制器工廠是去掃描bin目錄下的全部程序集) //2.1 從當前運行的bin目錄下加載FB.CMS.MvcSite.dll程序集 Assembly controllerAss = Assembly.Load("FB.CMS.MvcSite"); //2.2 告訴AutoFac控制器工廠,控制器的建立從controllerAss中查找(注意:RegisterControllers()方法是一個可變參數,若是你的控制器類的建立須要去多個程序集中查找的話,
那麼咱們就再用Assembly controllerBss=Assembly.Load("須要的程序集名")加載須要的程序集,而後與controllerAss組成數組,而後將這個數組傳遞到RegisterControllers()方法中) builder.RegisterControllers(controllerAss); //第三步:告訴AutoFac容器,建立項目中的指定類的對象實例,以接口的形式存儲(其實就是建立數據倉儲層與業務邏輯層這兩個程序集中全部類的對象實例,而後以其接口的形式保存到AutoFac容器內存中,
固然若是有須要也能夠建立其餘程序集的全部類的對象實例,這個只須要咱們指定就能夠了) //3.1 加載數據倉儲層FB.CMS.Repository這個程序集。 Assembly repositoryAss = Assembly.Load("FB.CMS.Repository"); //3.2 反射掃描這個FB.CMS.Repository.dll程序集中全部的類,獲得這個程序集中全部類的集合。 Type[] rtypes = repositoryAss.GetTypes(); //3.3 告訴AutoFac容器,建立rtypes這個集合中全部類的對象實例 builder.RegisterTypes(rtypes) .AsImplementedInterfaces(); //指明建立的rtypes這個集合中全部類的對象實例,以其接口的形式保存 //3.4 加載業務邏輯層FB.CMS.Services這個程序集。 Assembly servicesAss = Assembly.Load("FB.CMS.Services"); //3.5 反射掃描這個FB.CMS.Services.dll程序集中全部的類,獲得這個程序集中全部類的集合。 Type[] stypes = servicesAss.GetTypes(); //3.6 告訴AutoFac容器,建立stypes這個集合中全部類的對象實例 builder.RegisterTypes(stypes) .AsImplementedInterfaces(); //指明建立的stypes這個集合中全部類的對象實例,以其接口的形式保存 //第四步:建立一個真正的AutoFac的工做容器 var container = builder.Build(); //咱們已經建立了指定程序集的全部類的對象實例,並以其接口的形式保存在AutoFac容器內存中了。那麼咱們怎麼去拿它呢? //從AutoFac容器內部根據指定的接口獲取其實現類的對象實例 //假設我要拿到IsysFunctionServices這個接口的實現類的對象實例,怎麼拿呢? //var obj = container.Resolve<IsysFunctionServices>(); //只有有特殊需求的時候能夠經過這樣的形式來拿。通常狀況下沒有必要這樣來拿,由於AutoFac會自動工做
(即:會自動去類的帶參數的構造函數中找與容器中key一致的參數類型,並將對象注入到類中,其實就是將對象賦值給構造函數的參數) //第五步:將當前容器中的控制器工廠替換掉MVC默認的控制器工廠。(即:不要MVC默認的控制器工廠了,用AutoFac容器中的控制器工廠替代)此處使用的是將AutoFac工做容器交給MVC底層 (須要using System.Web.Mvc;) DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); //咱們知道控制器的建立是調用MVC默認的控制器工廠,默認的控制器工廠是調用控制器類的無參構造函數 //但是咱們若是要使用AutoFac自動工廠,將對象經過構造函數注入類中,那麼這個構造函數就須要帶參數 //若是咱們將控制器的無參構造函數刪除,保留帶參數的構造函數,MVC默認的控制器工廠來建立控制的時候 //就會去調用無參的構造函數,但是這時候發現沒有無參的構造函數因而就報「沒有爲該對象定義無參數的構造函數」錯誤 //既然報錯,那咱們若是保留無參的構造函數,同時在聲明一個帶參數的構造函數是否可行呢? //答案;行是行,可是建立控制器的時候,MVC默認的控制器工廠調用的是無參構造函數,它並不會去調用有參的構造函數 //這時候,咱們就只能將AutoFac它的控制器工廠替換調用MVC默認的控制器工廠(控制器由AutoFac的控制器工廠來建立) //而AutoFac控制器工廠在建立控制的時候只會掃描帶參數的構造函數,並將對象注入到帶參數的構造函數中 //AutofacDependencyResolver這個控制器工廠是繼承了 IDependencyResolver接口的,而IDependencyResolver接口是MVC的東西 //MVC默認的控制器工廠名字叫:DefaultControllerFactory //具體參考:http://www.cnblogs.com/artech/archive/2012/04/01/controller-activation-032.html } } }
using FB.CMS.MvcSite.App_Start; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace FB.CMS.MvcSite { // 注意: 有關啓用 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); //第一: 在網站一啓動的時候就初始化AutoFac的相關功能 /* 1.0 告訴AutoFac初始化數據倉儲層FB.CMS.Repository.dll中全部類的對象實例。這些對象實例以其接口的形式保存在AutoFac容器中 2.0 告訴AutoFac初始化業務邏輯層FB.CMS.Services.dll中全部類的對象實例。這些對象實例以其接口的形式保存在AutoFac容器中 3.0 將MVC默認的控制器工廠替換成AutoFac的工廠 */ //具體作法就是咱們去App_Start文件夾下建立一個AutoFacConfig類,具體實現什麼功能去這個類中實現。而後再這裏調用下這個類 AutoFacConfig.Register(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace FB.CMS.MvcSite.Controllers { using FB.CMS.IServices; public class HomeController : Controller { IsysFunctionServices dal; public HomeController(IsysFunctionServices dal) //依賴構造函數進行對象注入 { this.dal = dal; //在構造函數中初始化HomeController控制器類的dal屬性 (這個dal屬性的類型是IsysFunctionServices) } public ActionResult Index() { var a = dal.QueryWhere(r => r.fID > 20).ToList(); //查詢 return View(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FB.CMS.Repository { using FB.CMS.IRepository; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq.Expressions; using System.Runtime.Remoting.Messaging; using System.Threading; public class BaseDal<TEntity> : IBaseDal<TEntity> where TEntity : class { //BaseDbContext db = new BaseDbContext(); //對建立上下文容器類對象進行優化(原理:一個線程下咱們只建立一個上下文容器類對象,而後保存到線程緩存當中去,當同一個線程過來的時候,就從線程緩存當中取上下文容器類對象) public BaseDbContext db { get { //獲取BaseDbContext的徹底限定名,其實這個名字沒什麼特別的意義,僅僅是一個名字而已,也能夠取別的名字的 string threadName = typeof(BaseDbContext).FullName; //獲取key爲threadName的這個線程緩存(CallContext就是線程緩存容器類) object dbObj = CallContext.GetData(threadName); //若是key爲threadName的線程緩存不存在 if (dbObj == null) { //建立BaseDbContext類的對象實例 dbObj = new BaseDbContext(); //將這個BaseDbContext類的對象實例保存到線程緩存當中(以鍵值對的形式進行保存的,我這就將key設爲當前線程的徹底限定名了) CallContext.SetData(threadName, dbObj); return dbObj as BaseDbContext; } return dbObj as BaseDbContext; } } DbSet<TEntity> _dbset; public BaseDal() { this._dbset = db.Set<TEntity>(); //初始化 } #region 增長 public void AddEnity(TEntity model) { if (model == null) { throw new Exception("moddel不能爲null"); } this._dbset.Add(model); } #endregion #region 物理刪除 /// <summary> /// 刪除 /// </summary> /// <param name="model">實體類</param> /// <param name="isaddedContext">是否物理刪除</param> public void DeleteEntity(TEntity model, bool isaddedContext) { if (model == null) { throw new Exception("DeleteEntity方法中的model不能爲null"); } //若是僅僅是邏輯刪除的話,那咱們只要調用編輯方法將標識爲邏輯刪除的那個字段修改成true就能夠了。 if (isaddedContext == true) { this._dbset.Attach(model); } this._dbset.Remove(model); } #endregion #region 查尋 /// <summary> /// 普通帶條件查詢 /// </summary> /// <param name="where"></param> /// <returns></returns> public IQueryable<TEntity> QueryWhere(Expression<Func<TEntity, bool>> where) { return this._dbset.Where(where); } /// <summary> /// 連表查詢 /// </summary> /// <param name="where">連表查詢的條件篩選查詢</param> /// <param name="tablesName">要作連表查詢的全部表名集合</param> /// <returns></returns> public IQueryable<TEntity> QueryJoin(Expression<Func<TEntity, bool>> where, string[] tablesName) { if (tablesName == null || tablesName.Any() == false) { throw new Exception("連表查詢最少也要一個表,全部QueryJoin方法中tablesName中最少也須要有一個表名"); } DbQuery<TEntity> query = this._dbset; foreach (string tableName in tablesName) { //不斷的連表,直到把tablesName裏的全部表都連完 query = query.Include(tableName); } return query.Where(where); //而後對連表進行條件篩選查詢 } /// <summary> /// 帶條件的分頁查詢 /// </summary> /// <typeparam name="TKey">按哪一個字段進行排序</typeparam> /// <param name="pageindex">當前頁</param> /// <param name="pagesize">頁大小</param> /// <param name="rowCount">數據總條數</param> /// <param name="order">排序</param> /// <param name="where">篩選條件</param> /// <returns></returns> public IQueryable<TEntity> QueryByPage<TKey>(int pageindex, int pagesize, out int rowCount, Expression<Func<TEntity, TKey>> order, Expression<Func<TEntity, bool>> where) { //獲取總條數 rowCount = this._dbset.Count(where); //建議將這個Where條件語句放在前面,若是你放到後面,分頁的時候可能存在問題。 return this._dbset.Where(where).OrderByDescending(order).Skip((pageindex - 1) * pagesize).Take(pagesize); } /// <summary> /// 調用存儲過程或執行SQL語句(可是咱們不推薦執行sql語句) /// </summary> /// <typeparam name="TElement"> /// 由於存儲過程返回的數據不必定就是TEntity這個實體,由於存儲過返回的結果集有多是本身拼接出來的,因此這個方法的返回結果 /// 爲Lsit<TEntity>就不合適了。 這個 TElement是在調用的存儲過程的時候傳入的一個實體,此實體必須和調用的存儲過程的返回結集 /// 中的字段名稱保存一致(你這個存儲過程返回有多個字段,那麼你這個實體中就應該有多少個屬性) /// </typeparam> /// <param name="sql"> /// 假設我建立了這麼一個存儲過程: /// create proc proc_T_UserInfo_Paging2(@pageSize int,@currentPage int,@CountData ) /// 那如今咱們調用這個存儲過程,那麼這個SQL語句的寫法就是: /// proc_T_UserInfo_Paging2 @pageSize int,@currentPage int,@CountData /// /// </param> /// <param name="prms">參數化查詢的參數數組</param> /// <returns></returns> public List<TElement> RunProc<TElement>(string sql, params object[] prms) { return db.Database.SqlQuery<TElement>(sql, prms).ToList(); } #endregion #region 編輯 /// <summary> /// 編輯 /// </summary> /// <param name="model">實體</param> /// <param name="propertyNames">要編輯的的全部屬性名稱序列</param> public void EditEntity(TEntity model, string[] propertyNames) { if (model == null) { throw new Exception("EditEntity方法中的參數model不能爲null"); } if (propertyNames.Any() == false || propertyNames == null) { throw new Exception("EditEntity方法中的參數propertyNames最少須要一個屬性"); } System.Data.Entity.Infrastructure.DbEntityEntry entry = db.Entry(model); entry.State = System.Data.EntityState.Unchanged; foreach (string item in propertyNames) { entry.Property(item).IsModified = true; } db.Configuration.ValidateOnSaveEnabled = false; } #endregion #region 統一保存 public int SaveChanges() { return db.SaveChanges(); } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace FB.CMS.Site.Controllers { using FB.CMS.IRepository; public class HomeController : Controller { IsysFunctionRepository fundal; IsysKeyValueRepository kvdal; //咱們在經過AutoFac將IsysFunctionRepository和IsysKeyValueRepository的兩個實現類的對象實例注入到當前構造函數中 //那麼在注入IsysFunctionRepository這實現類的對象的時候就會執行到sysFunctionRepository類,而這個sysFunctionRepository類 //又是繼承了BaseDal類,而後就會去執行BaseDal類的構造函數,而在BaseDal這個類的構造函數中咱們執行了 //this._dbset = db.Set<TEntity>()這段代碼,這段代碼中使用了db這個上下文容器類對象。既然使用了這個對象,因此就會首先初始化這個對象 //按照日常的寫法,通常都是直接new一個上下文容器對象,即:BaseDbContext db = new BaseDbContext(); //而後當咱們再對構造函數的參數IsysKeyValueRepository kvdal進行對象注入的時候,一樣的原理又會執行一次 //BaseDbContext db = new BaseDbContext();即又建立了一個上下文容器類對象。因此說當咱們經過構造函數一次性注入多個對象的時候 //會建立多個上下文容器類對象。因此這樣的作法是很是很差的。由於咱們若是在同一個線程裏面用兩個上下文容器類對象分別去對數據增刪改 //的後,【最後統一執行db.SaveChanges()】;這時候會發現只有後面的那個上下文容器類對象對數據的增刪改操做成功,而前面的那個上下文容器類對象對數據的增刪 public HomeController(IsysFunctionRepository fundal, IsysKeyValueRepository kvdal) { this.fundal = fundal; this.kvdal = kvdal; } public ActionResult Index() { //var model = fundal.QueryWhere(r => r.fID == 20).FirstOrDefault(); //model.fName = "默認888"; //fundal.SaveChanges(); //執行保存修改其實就已經操做一次數據庫了 //var model2 = kvdal.QueryWhere(r => r.KID == 3).FirstOrDefault(); //model2.KName = "公司88"; //fundal.SaveChanges(); //這裏又執行一次保存修改操做,又執行了一個數據庫, //那咱們就但願在同一個控制器中(同一個控制中意味着在同一個線程當中)無論操做多少個上下文容器類對象對數據的操做 //咱們只但願最後只執行已給SaveChanges(),這樣就能保障最少次的對數據庫的操做。從而提升性能 //那咱們怎麼作呢?其實很簡單,當咱們執行構造函數對多個對象進行注入的時候,咱們保證只有一個上下文容器類對象實例 //當咱們在Home控制器中經過構造函數注入兩個對象,那麼這個Home控制器會有一個線程進行管理,那麼當這個線程過來的時候 //咱們就建立先去一個線程緩存中去獲取一下這個線程名對應的緩存,若是緩存不存在,那麼咱們就建立一個上下文容器類對象實例 //並將這個對象實例存儲到線程緩存當中,而後返回這個上下文容器類對象。若是緩存存在,那麼就直接獲取這個緩存進行返回 //這樣就保證了同一線程下,只有個上下文容器類實例對象,這樣咱們就能夠用任何上下文容器類對象對數據的增刪改後,最後只 //須要用任何一個上下文容器類對象執行一次SaveChanges()就能夠了 var model = fundal.QueryWhere(r => r.fID == 20).FirstOrDefault(); model.fName = "默認888"; //fundal.SaveChanges(); //執行保存修改其實就已經操做一次數據庫了 var model2 = kvdal.QueryWhere(r => r.KID == 3).FirstOrDefault(); model2.KName = "公司88"; fundal.SaveChanges(); //經過對BaseDal中對建立上下文容器類的優化後,咱們看到在同一個線程中 兩個上下文容器類對象(經過優化後其實這個兩個上下文容器類對象實際上是同一個上下文容器類對象)
同時對數據進行改的操做,最後只執行了一個fundal.SaveChanges(); 方法,就將數據成功保存了。這樣就達到了最少操做數據庫。性能提高了 return View(model); } } }
public ActionResult Index() { ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<UserInfoSevices>(); //想拿到UserInfoSevices類的實例 builder.RegisterType<UserInfoRepository>().As<IUserInfoRepository>(); //與之關聯的UserInfoRepository類也須要拿到,並轉化成接口的形式保存 var aa = builder.Build().Resolve<UserInfoSevices>().QueryModel(r => r.Age > 0); //在這裏使用 return View(); }
C# MVC 基於From的身份驗證
event 和delegate的分別
忽然想起delegate委託是支持+= 和-=操做的,而後研究一下究竟這個是怎麼作到的,好模仿一下。一開始覺得是+=的運算符重載,可是在類庫參考中並無這個運算符重載,只有!= 和==運算符重載。有點納悶,最終發現,原來+=這些直接就是語法層面的實現,只是針對delegate纔有的福利,因此也不存在模仿的可能性了。
順便就總結一下event事件和delegate委託的區別。這個區別有點相似字段和屬性的不一樣。好比接口能夠有屬性,可是不能有字段。爲何?由於屬性本質是方法,對字段進行了包裝,這種包裝有一點語法上的支持,在不一樣上下文下,會有不一樣的解釋意義。
委託本質上是函數指針,是一個存儲函數地址的變量,它很靈活,強大,這和字段同樣,若是人們想要限制外部環境直接訪問和控制委託,那麼就能夠經過event來達到這個目的。
固然,事件對委託的包裝,添加了一層語義,那就是爲了實現「事件模式」,若是你不想實現事件模式,那麼就應該用普通函數來自定義這層包裝。通常來講,事件的套路很適合程序員的須要。
delegate | event |
賦值= 取值= 添加回調函數+= 移除回調函數-= 調用() |
添加處理函數+= 移除處理函數-= (若是沒有單獨定義事件的委託,在內部,事件能夠等同委託來用) |
由於委託能夠綁定多個函數,那麼它的返回值到底是哪一個?返回值是它綁定的最後一個回調函數。
常見的異步方式async 和 await
以前研究過c#的async和await關鍵字,幕後幹了什麼,可是不知道爲何找不到相關資料了。如今從新研究一遍,順便記錄下來,方便之後查閱。
基礎知識
async 關鍵字標註一個方法,該方法返回值是一個Task、或者Task<TResult>、void、包含GetAwaiter方法的類型。該方法一般包含一個await表達式。該表達式標註一個點,將被某個異步方法回跳到該點。而且,當前函數執行到該點,將馬上返回控制權給調用方。
以上描述了async方法想幹的事情,至於如何實現,這裏就不涉獵了。
我的看法
由此能夠知道,async 和await關鍵字主要目的是爲了控制異步線程的同步,讓一個異步過程,表現得好像同步過程同樣。
好比async 方法分n個任務去下載網頁並進行處理:先await下載,而後馬上返回調用方,以後的處理就由異步線程完成下載後調用。這時候調用方能夠繼續執行它的任務,不過,若是調用方馬上就須要async的結果,那麼應該就只能等待,不過大多數狀況:他暫時不須要這個結果,那麼就能夠並行處理這些代碼。
可見,並行性體如今await 上,若是await 點和最終的數據結果距離越遠,那麼並行度就越高。若是await的點越多,相信也會改善並行性。
資料顯示,async 和await 關鍵字並不會建立線程,這是很關鍵的一點。他們只是建立了一個返回點,提供給須要他的線程使用。那麼線程到底是誰建立?注意await 表達式的組成,他須要一個Task,一個Task並不表明必定要建立線程,也能夠是另外一個async方法,可是層層包裹最裏面的方法,極可能就是一個原生的Task,好比await Task.Run(()=>Thread.Sleep(0)); ,這個真正產生線程的語句,就會根據前面那些await點,逐個回調。
從這點來看,async 方法,未必就是一個異步方法,他在語義上更加貼近「非阻塞」, 當遇到阻塞操做,馬上用await定點返回,至於其餘更深一層的解決手段,它就不關心了。這是程序員須要關心的,程序員須要用真正的建立線程代碼,來完成異步操做(固然這一步可由庫程序員完成)。
注意async的幾個返回值類型,這表明了不一樣的使用場景。若是是void,說明客戶端不關心數據同步問題,它只須要線程的控制權馬上返回。能夠用在ui 等場合,若是是Task,客戶端也不關心數據,可是它但願可以控制異步線程,這多是對任務執行順序有必定的要求。固然,最多見的是Task<TResult>。
綜上,async和await並非爲了多任務而設計的,若是追求高併發,應該在async函數內部用Task好好設計一番。在使用async 和await的時候,只須要按照非阻塞的思路去編寫代碼就能夠了,至於幕後怎麼處理就交給真正的多線程代碼建立者吧。
示範代碼
static async Task RunTaskAsync(int step) { for(int i=0; i < step; i++) { await Task.Run(()=>Thread.Sleep(tmloop));//點是靜態的,依次執行 Thread.Sleep(tm2); } Thread.Sleep(tm3); } //客戶端 Task tk= RunTaskAsync(step); Thread.Sleep(tm1);//這一段是並行的,取max(函數,代碼段)最大時間 tk.Wait( );//這裏表明最終數據
爲了達到高度並行,應該用真正的多線程代碼:
static async Task RunTaskByParallelAsync(int step) { await Task.Run(()=>Parallel.For(0,step, s=>{loop(tmloop); loop(tm2); } )); loop(tm3); }
並行編碼方法
並行執行有幾個方法,第一個是建立n個Task,一塊兒啓動。問題是怎麼處理await點。每一個task寫一個await點是不行的,由於遇到第一個await就馬上返回,而不會開啓全部任務並行執行。所以await不能隨便放。那麼如何爲一組Task設定await點呢?能夠經過Task.WhenAll 這個方法,他會等待一組Task執行完畢返回。
特定狀況下,能夠用Parallel.For 來開啓一組任務,可是這個類並無實現async模式,也就是它會阻塞當前線程,因此須要用一個Task來包裹它。
可見,非阻塞和並行不徹底是一回事。
C# Task用法
一、Task的優點
ThreadPool相比Thread來講具有了不少優點,可是ThreadPool卻又存在一些使用上的不方便。好比:
◆ ThreadPool不支持線程的取消、完成、失敗通知等交互性操做;
◆ ThreadPool不支持線程執行的前後次序;
以往,若是開發者要實現上述功能,須要完成不少額外的工做,如今,FCL中提供了一個功能更強大的概念:Task。Task在線程池的基礎上進行了優化,並提供了更多的API。在FCL4.0中,若是咱們要編寫多線程程序,Task顯然已經優於傳統的方式。
如下是一個簡單的任務示例:
![](http://static.javashuo.com/static/loading.gif)
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Task t = new Task(() => { Console.WriteLine("任務開始工做……"); //模擬工做過程 Thread.Sleep(5000); }); t.Start(); t.ContinueWith((task) => { Console.WriteLine("任務完成,完成時候的狀態爲:"); Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); }); Console.ReadKey(); } } }
二、Task的用法
2.一、建立任務
無返回值的方式
方式1:
var t1 = new Task(() => TaskMethod("Task 1"));
t1.Start();
Task.WaitAll(t1);//等待全部任務結束
注:
任務的狀態:
Start以前爲:Created
Start以後爲:WaitingToRun
方式2:
Task.Run(() => TaskMethod("Task 2"));
方式3:
Task.Factory.StartNew(() => TaskMethod("Task 3")); 直接異步的方法
或者
var t3=Task.Factory.StartNew(() => TaskMethod("Task 3"));
Task.WaitAll(t3);//等待全部任務結束
注:
任務的狀態:
Start以前爲:Running
Start以後爲:Running
![](http://static.javashuo.com/static/loading.gif)
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var t1 = new Task(() => TaskMethod("Task 1")); var t2 = new Task(() => TaskMethod("Task 2")); t2.Start(); t1.Start(); Task.WaitAll(t1, t2); Task.Run(() => TaskMethod("Task 3")); Task.Factory.StartNew(() => TaskMethod("Task 4")); //標記爲長時間運行任務,則任務不會使用線程池,而在單獨的線程中運行。 Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning); #region 常規的使用方式 Console.WriteLine("主線程執行業務處理."); //建立任務 Task task = new Task(() => { Console.WriteLine("使用System.Threading.Tasks.Task執行異步操做."); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } }); //啓動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主線程執行其餘處理"); task.Wait(); #endregion Thread.Sleep(TimeSpan.FromSeconds(1)); Console.ReadLine(); } static void TaskMethod(string name) { Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } } }
async/await的實現方式:
![](http://static.javashuo.com/static/loading.gif)
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { async static void AsyncFunction() { await Task.Delay(1); Console.WriteLine("使用System.Threading.Tasks.Task執行異步操做."); for (int i = 0; i < 10; i++) { Console.WriteLine(string.Format("AsyncFunction:i={0}", i)); } } public static void Main() { Console.WriteLine("主線程執行業務處理."); AsyncFunction(); Console.WriteLine("主線程執行其餘處理"); for (int i = 0; i < 10; i++) { Console.WriteLine(string.Format("Main:i={0}", i)); } Console.ReadLine(); } } }
帶返回值的方式
方式4:
Task<int> task = CreateTask("Task 1");
task.Start();
int result = task.Result;
![](http://static.javashuo.com/static/loading.gif)
using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static Task<int> CreateTask(string name) { return new Task<int>(() => TaskMethod(name)); } static void Main(string[] args) { TaskMethod("Main Thread Task"); Task<int> task = CreateTask("Task 1"); task.Start(); int result = task.Result; Console.WriteLine("Task 1 Result is: {0}", result); task = CreateTask("Task 2"); //該任務會運行在主線程中 task.RunSynchronously(); result = task.Result; Console.WriteLine("Task 2 Result is: {0}", result); task = CreateTask("Task 3"); Console.WriteLine(task.Status); task.Start(); while (!task.IsCompleted) { Console.WriteLine(task.Status); Thread.Sleep(TimeSpan.FromSeconds(0.5)); } Console.WriteLine(task.Status); result = task.Result; Console.WriteLine("Task 3 Result is: {0}", result); #region 常規使用方式 //建立任務 Task<int> getsumtask = new Task<int>(() => Getsum()); //啓動任務,並安排到當前任務隊列線程中執行任務(System.Threading.Tasks.TaskScheduler) getsumtask.Start(); Console.WriteLine("主線程執行其餘處理"); //等待任務的完成執行過程。 getsumtask.Wait(); //得到任務的執行結果 Console.WriteLine("任務執行結果:{0}", getsumtask.Result.ToString()); #endregion } static int TaskMethod(string name) { Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(2)); return 42; } static int Getsum() { int sum = 0; Console.WriteLine("使用Task執行異步操做."); for (int i = 0; i < 100; i++) { sum += i; } return sum; } } }
async/await的實現:
![](http://static.javashuo.com/static/loading.gif)
using System; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { public static void Main() { var ret1 = AsyncGetsum(); Console.WriteLine("主線程執行其餘處理"); for (int i = 1; i <= 3; i++) Console.WriteLine("Call Main()"); int result = ret1.Result; //阻塞主線程 Console.WriteLine("任務執行結果:{0}", result); } async static Task<int> AsyncGetsum() { await Task.Delay(1); int sum = 0; Console.WriteLine("使用Task執行異步操做."); for (int i = 0; i < 100; i++) { sum += i; } return sum; } } }
2.二、組合任務.ContinueWith
簡單Demo:
![](http://static.javashuo.com/static/loading.gif)
任務的串行:
![](http://static.javashuo.com/static/loading.gif)
子任務:
![](http://static.javashuo.com/static/loading.gif)
動態並行(TaskCreationOptions.AttachedToParent) 父任務等待全部子任務完成後 整個任務纔算完成
![](http://static.javashuo.com/static/loading.gif)
2.三、取消任務 CancellationTokenSource
![](http://static.javashuo.com/static/loading.gif)
2.四、處理任務中的異常
單個任務:
![](http://static.javashuo.com/static/loading.gif)
多個任務:
![](http://static.javashuo.com/static/loading.gif)
async/await的方式:
![](http://static.javashuo.com/static/loading.gif)
2.五、Task.FromResult的應用
![](http://static.javashuo.com/static/loading.gif)
2.六、使用IProgress實現異步編程的進程通知
IProgress<in T>只提供了一個方法void Report(T value),經過Report方法把一個T類型的值報告給IProgress,而後IProgress<in T>的實現類Progress<in T>的構造函數接收類型爲Action<T>的形參,經過這個委託讓進度顯示在UI界面中。
![](http://static.javashuo.com/static/loading.gif)
2.七、Factory.FromAsync的應用 (簡APM模式(委託)轉換爲任務)(BeginXXX和EndXXX)
帶回調方式的
![](http://static.javashuo.com/static/loading.gif)
不帶回調方式的
![](http://static.javashuo.com/static/loading.gif)
c#源碼的執行過程
我想也許要寫些東西,記錄我作程序員的日子吧
================================================
要講到C#源碼的執行過程 首先要提下程序集,由於Clr並非和託管摸塊打交道的,而是和程序集(dll,exe)
一、從哪裏來
程序集是由一個或者多個託管模塊以及 資源文件等共同組成的,C#編譯器(csc.exe)再把源碼編程成IL代碼和元數據的時候,會進一步連同資源文件合併成程序集,
實際上就是個PE32文件,裏面包含一個清單文件 和多個託管模塊和資源(如圖),另外程序集中還有一些自描述信息。
二、執行過程
編譯器生成好程序集之後,若是是可執行的程序集,會在Main方法執行以前,window會預先讀取程序集的頭文件(pe32),若是是x86則開一個32位的進程,x64的就開一個64位的進程
而後在進程空間裏面加載MSCOREE.DLL的x86 或者x64版本或者arm版本,而後進程的主線程會調用MSCOREE.DLL的一個方法,初始化Clr,而Clr會加載程序集exe,再調用其入口方法Main。
3.Main方法內部執行
在Main執行以前,Clr 會檢測出方法引用的全部類型,(Console),而後在內存中分配對應數據類型的空間,這個地址裏面包含着這個類型全部的方法聲名,每一項都對應着Clr裏面的一個未編檔函數(JITCompiler)
首次運行Main方法的試試JITCompiler會被調用,在這個方法裏面1,負責在方法的實現類型中(console)程序集元數據中查詢該方法的IL方法 2,動態分配內存塊 3,把IL編譯成本機Cpu的指令,存儲到動態分配的空間裏面
4,修改這個條目的地址,使它指向動態分配的地址 5.跳轉到內存塊中的本機代碼執行,這時候執行的就是IL代碼的cpu機器碼
5.在次執行Console.WriteLine的時候,就不會運行JITCompiler,直接運行機器碼