這篇文章的主題並不是鼓勵不使用繼承,而是僅從使用繼承帶來的問題出發,討論繼承機制不太好的地方,從而在使用時慎重選擇,避開可能遇到的坑。css
JAVA中使用到繼承就會有兩個沒法迴避的缺點:html
- 打破了封裝性,子類依賴於超類的實現細節,和超類耦合。
- 超類更新後可能會致使錯誤。
繼承打破了封裝性
關於這一點,下面是一個詳細的例子(來源於Effective Java第16條)java
public class MyHashSet<E> extends HashSet<E> { private int addCount = 0; public int getAddCount() { return addCount; } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } }
這裏自定義了一個HashSet
,重寫了兩個方法,它和超類惟一的區別是加入了一個計數器,用來統計添加過多少個元素。git
寫一個測試來測試這個新增的功能是否工做:github
public class MyHashSetTest { private MyHashSet<Integer> myHashSet = new MyHashSet<Integer>(); @Test public void test() { myHashSet.addAll(Arrays.asList(1,2,3)); System.out.println(myHashSet.getAddCount()); } }
運行後會發現,加入了3個元素以後,計數器輸出的值是6。web
進入到超類中的addAll()
方法就會發現出錯的緣由:它內部調用的是add()
方法。因此在這個測試裏,進入子類的addAll()
方法時,數器加3,而後調用超類的addAll()
,超類的addAll()
又會調用子類的add()
三次,這時計數器又會再加三。正則表達式
問題的根源
將這種狀況抽象一下,能夠發現出錯是由於超類的可覆蓋的方法存在自用性(即超類裏可覆蓋的方法調用了別的可覆蓋的方法),這時候若是子類覆蓋了其中的一些方法,就可能致使錯誤。算法
好比上圖這種狀況,Father
類裏有可覆蓋的方法A
和方法B
,而且A
調用了B
。子類Son
重寫了方法B
,這時候若是子類調用繼承來的方法A
,那麼方法A
調用的就再也不是Father.B()
,而是子類中的方法Son.B()
。若是程序的正確性依賴於Father.B()
中的一些操做,而Son.B()
重寫了這些操做,那麼就極可能致使錯誤產生。sql
關鍵在於,子類的寫法極可能從表面上看來沒有問題,可是卻會出錯,這就迫使開發者去了解超類的實現細節,從而打破了面向對象的封裝性,由於封裝性是要求隱藏實現細節的。更危險的是,錯誤不必定能輕易地被測出來,若是開發者不瞭解超類的實現細節就進行重寫,那麼可能就埋下了隱患。數據庫
超類更新時可能產生錯誤
這一點比較好理解,主要有如下幾種可能:
- 超類更改了已有方法的簽名。會致使編譯錯誤。
- 超類新增了方法:
- 和子類已有方法的簽名相同但返回類型不一樣,會致使編譯錯誤。
- 和子類的已有方法簽名相同,會致使子類無心中複寫,回到了第一種狀況。
- 和子類無衝突,但可能會影響程序的正確性。好比子類中元素加入集合必需要知足特定條件,這時候若是超類加入了一個無需檢測就能夠直接將元素插入的方法,程序的正確性就受到了威脅。
設計可繼承的類
設計能夠用來繼承的類時,應該注意:
- 對於存在自用性的可覆蓋方法,應該用文檔精確描述調用細節。
- 儘量少的暴露受保護成員,不然會暴露太多實現細節。
- 構造器不該該調用任何可覆蓋的方法。
詳細解釋下第三點。它實際上和 繼承打破了封裝性 裏討論的問題很類似,假設有如下代碼:
public class Father { public Father() { someMethod(); } public void someMethod() { } }
public class Son extends Father { private Date date; public Son() { this.date = new Date(); } @Override public void someMethod() { System.out.println("Time = " + date.getTime()); } }
上述代碼在運行測試時就會拋出NullPointerException
:
public class SonTest { private Son son = new Son(); @Test public void test() { son.someMethod(); } }
由於超類的構造函數會在子類的構造函數以前先運行,這裏超類的構造函數對someMethod()
有依賴,同時someMethod()
被重寫,因此超類的構造函數裏調用到的將是Son.someMethod()
,而這時候子類還沒被初始化,因而在運行到date.getTime()
時便拋出了空指針異常。
所以,若是在超類的構造函數裏對可覆蓋的方法有依賴,那麼在繼承時就可能會出錯。
結論
繼承有不少優勢,但使用繼承時應該慎重並多加考慮。一樣用來實現代碼複用的還有複合,若是使用繼承和複合皆可(這是前提),那麼應該優先使用複合,由於複合能夠保持超類對實現細節的屏蔽,上述關於繼承的缺點均可以用複合來避免。這也是所謂的複合優先於繼承。
若是使用繼承,那麼應該留意重寫超類中存在自用性的可覆蓋方法可能會出錯,即便不進行重寫,超類更新時也可能會引入錯誤。同時也應該精心設計超類,對任何相互調用的可覆蓋方法提供詳細文檔。
C# 語言歷史版本特性(C# 1.0到C# 8.0彙總)
歷史版本
C#做爲微軟2000年之後.NET平臺開發的當家語言,發展至今具備17年的歷史,語言自己具備豐富的特性,微軟對其更新支持也十分支持。微軟將C#提交給標準組織ECMA,C# 5.0目前是ECMA發佈的最新規範,C# 6.0仍是草案階段,C# 7.1是微軟當前提供的最新規範。
這裏僅僅列個提綱,因爲C# 5.0是具備ECMA標準規範的版本,因此選擇C# 5.0做爲主要版本學習,並專題學習C# 6.0,7.0版本新特性。
C#語言規範GitHub庫參見:https://github.com/dotnet/csharplang
C#語言路線圖及開發中的特性參見:
https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md
語言版本 | 發佈時間 | .NET Framework要求 | Visual Studio版本 |
---|---|---|---|
C# 1.0 | 2002.1 | .NET Framework 1.0 | Visual Studio .NET 2002 |
C# 1.1\1.2 | 2003.4 | .NET Framework 1.1 | Visual Studio .NET 2003 |
C# 2.0 | 2005.11 | .NET Framework 2.0 | Visual Studio 2005 |
C# 3.0 | 2007.11 | .NET Framework 2.0\3.0\3.5 | Visual Studio 2008 |
C# 4.0 | 2010.4 | .NET Framework 4.0 | Visual Studio 2010 |
C# 5.0 | 2012.8 | .NET Framework 4.5 | Visual Studio 2012\2013 |
C# 6.0 | 2015.7 | .NET Framework 4.6 | Visual Studio 2015 |
C# 7.0 | 2017.3 | .NET Framework 4.6.2 | Visual Studio 2017 |
C# 7.1 | 2017.6 | .NET Framework | Visual Studio 2017 v15.3預覽版 |
C# 8.0 | 待發布 | .NET Framework 4.7.1 | Visual Studio 2017 v15.7 |
C# 1.0 特性
第1個版本,編程語言最基礎的特性。
- Classes:面向對象特性,支持類類型
- Structs:結構
- Interfaces:接口
- Events:事件
- Properties:屬性,類的成員,提供訪問字段的靈活方法
- Delegates:委託,一種引用類型,表示對具備特定參數列表和返回類型的方法的引用
- Expressions,Statements,Operators:表達式、語句、操做符
- Attributes:特性,爲程序代碼添加元數據或聲明性信息,運行時,經過反射能夠訪問特性信息
- Literals:字面值(或理解爲常量值),區別常量,常量是和變量相對的
C# 2特性 (VS 2005)
- Generics:泛型
- Partial types:分部類型,能夠將類、結構、接口等類型定義拆分到多個文件中
- Anonymous methods:匿名方法
- Iterators:迭代器
- Nullable types:能夠爲Null的類型,該類能夠是其它值或者null
- Getter/setter separate accessibility:屬性訪問控制
- Method group conversions (delegates):方法組轉換,能夠將聲明委託表明一組方法,隱式調用
- Co- and Contra-variance for delegates and interfaces:委託、接口的協變和逆變
- Static classes:靜態類
- Delegate inference:委託推斷,容許將方法名直接賦給委託變量
C# 3特性 (VS 2008)
- Implicitly typed local variables:
- Object and collection initializers:對象和集合初始化器
- Auto-Implemented properties:自動屬性,自動生成屬性方法,聲明更簡潔
- Anonymous types:匿名類型
- Extension methods:擴展方法
- Query expressions:查詢表達式
- Lambda expression:Lambda表達式
- Expression trees:表達式樹,以樹形數據結構表示代碼,是一種新數據類型
- Partial methods:部分方法
C# 4特性 (VS 2010)
- Dynamic binding:動態綁定
- Named and optional arguments:命名參數和可選參數
- Generic co- and contravariance:泛型的協變和逆變
- Embedded interop types (「NoPIA」):開啓嵌入類型信息,增長引用COM組件程序的中立性
C# 5特性 (VS 2012)
- Asynchronous methods:異步方法
- Caller info attributes:調用方信息特性,調用時訪問調用者的信息
C# 6特徵 (VS 2015)
- Compiler-as-a-service (Roslyn)
- Import of static type members into namespace:支持僅導入類中的靜態成員
- Exception filters:異常過濾器
- Await in catch/finally blocks:支持在catch/finally語句塊使用await語句
- Auto property initializers:自動屬性初始化
- Default values for getter-only properties:設置只讀屬性的默認值
- Expression-bodied members:支持以表達式爲主體的成員方法和只讀屬性
- Null propagator (null-conditional operator, succinct null checking):Null條件操做符
- String interpolation:字符串插值,產生特定格式字符串的新方法
- nameof operator:nameof操做符,返回方法、屬性、變量的名稱
- Dictionary initializer:字典初始化
C# 7 特徵 (Visual Studio 2017)
- Out variables:out變量直接聲明,例如能夠out in parameter
- Pattern matching:模式匹配,根據對象類型或者其它屬性實現方法派發
- Tuples:元組
- Deconstruction:元組解析
- Discards:沒有命名的變量,只是佔位,後面代碼不須要使用其值
- Local Functions:局部函數
- Binary Literals:二進制字面量
- Digit Separators:數字分隔符
- Ref returns and locals:引用返回值和局部變量
- Generalized async return types:async中使用泛型返回類型
- More expression-bodied members:容許構造器、解析器、屬性能夠使用表達式做爲body
- Throw expressions:Throw能夠在表達式中使用
C# 7.1 特徵 (Visual Studio 2017 version 15.3)
- Async main:在main方法用async方式
- Default expressions:引入新的字面值default
- Reference assemblies:
- Inferred tuple element names:
- Pattern-matching with generics:
C# 8.0 特徵 (Visual Studio 2017 version 15.7)
- Default Interface Methods 缺省接口實現
- Nullable reference type NullableReferenceTypes 非空和可控的數據類型
- Recursive patterns 遞歸模式
- Async streams 異步數據流
- Caller expression attribute 調用方法表達式屬性
- Target-typed new
- Generic attributes 通用屬性
- Ranges
- Default in deconstruction
- Relax ordering of ref and partial modifiers
-
SQL Server事務 事務日誌
事務 (SQL Server)
1、事務概念
事務是一種機制、是一種操做序列,它包含了一組數據庫操做命令,這組命令要麼所有執行,要麼所有不執行。所以事務是一個不可分割的工做邏輯單元。在數據庫系統上執行併發操做時事務是做爲最小的控制單元來使用的。這特別適用於多用戶同時操做的數據通訊系統。例如:訂票、銀行、保險公司以及證券交易系統等。
2、事務屬性
事務4大屬性:
1 原子性(Atomicity):事務是一個完整的操做。
2 一致性(Consistency):當事務完成時,數據必須處於一致狀態。
3 隔離性(Isolation):對數據進行修改的全部併發事務是彼此隔離的。
4 持久性(Durability):事務完成後,它對於系統的影響是永久性的。
3、建立事務
T-SQL中管理事務的語句:
1 開始事務: begin transaction
2 提交事務:commit transaction
3 回滾事務: rollback transaction
事務分類:
1 顯式事務:用begin transaction明確指定事務的開始。
2 隱性事務:打開隱性事務:set implicit_transactions on,當以隱性事務模式操做時,SQL Servler將在提交或回滾事務後自動啓動新事務。沒法描述事務的開始,只須要提交或回滾事務。
3 自動提交事務:SQL Server的默認模式,它將每條單獨的T-SQL語句視爲一個事務。若是成功執行,則自動提交,不然回滾。事務日誌 (SQL Server)
每一個 SQL Server 數據庫都有事務日誌,用於記錄全部事務以及每一個事務所作的數據庫修改。
事務日誌是數據庫的一個關鍵組件。 若是系統出現故障,你將須要依靠該日誌將數據庫恢復到一致的狀態。
重要
永遠不要刪除或移動此日誌,除非你徹底瞭解執行此操做的後果。
提示
檢查點會建立一些正常點,在數據庫恢復期間將從這些正常點開始應用事務日誌。 有關詳細信息,請參閱數據庫檢查點 (SQL Server)。
事務日誌支持的操做
事務日誌支持如下操做:
-
恢復個別的事務。
-
在 SQL Server 啓動時恢復全部未完成的事務。
-
將還原的數據庫、文件、文件組或頁前滾至故障點。
-
支持事務複製。
-
支持高可用性和災難恢復解決方案: AlwaysOn 可用性組、數據庫鏡像和日誌傳送。
恢復個別的事務
若是應用程序發出 ROLLBACK 語句,或者數據庫引擎檢測到錯誤(例如失去與客戶端的通訊),使用日誌記錄回退未完成的事務所作的修改。
在 SQL Server 啓動時恢復全部未完成的事務
當服務器發生故障時,數據庫可能處於這樣的狀態:尚未將某些修改從緩存寫入數據文件,在數據文件內有未完成的事務所作的修改。 啓動 SQL Server 實例時,它將對每一個數據庫執行恢復操做。 前滾日誌中記錄的、可能還沒有寫入數據文件的每一個修改。 在事務日誌中找到的每一個未完成的事務都將回滾,以確保數據庫的完整性。
將還原的數據庫、文件、文件組或頁前滾至故障點
在硬件丟失或磁盤故障影響到數據庫文件後,能夠將數據庫還原到故障點。 先還原上次完整數據庫備份和上次差別數據庫備份,而後將後續的事務日誌備份序列還原到故障點。
還原每一個日誌備份時,數據庫引擎將從新應用日誌中記錄的全部修改,前滾全部事務。 最後的日誌備份還原後,數據庫引擎將使用日誌信息回退到該點上未完成的全部事務。
支持事務複製
日誌讀取器代理程序監視已爲事務複製配置的每一個數據庫的事務日誌,並將已設複製標記的事務從事務日誌複製到分發數據庫中。 有關詳細信息,請參閱 事務複製的工做原理。
支持高可用性和災難恢復解決方案
備用服務器解決方案、AlwaysOn 可用性組、數據庫鏡像和日誌傳送極大程度上依賴於事務日誌。
在 AlwaysOn 可用性組方案中,數據庫的每一個更新(主要副本)在數據庫的完整且獨立的副本(次要副本)中直接再現。 主要副本直接將每一個日誌記錄發送到次要副本,這可將傳入日誌記錄應用到可用性組數據庫,並不斷前滾。 有關詳細信息,請參閱 AlwaysOn 故障轉移羣集實例
在日誌傳送方案中,主服務器將主數據庫的活動事務日誌發送到一個或多個目標服務器。 每一個輔助服務器將該日誌還原爲其本地的輔助數據庫。 有關詳細信息,請參閱 關於日誌傳送。
在數據庫鏡像方案中,數據庫(主體數據庫)的每次更新都在獨立的、完整的數據庫(鏡像數據庫)副本中當即從新生成。 主體服務器實例當即將每一個日誌記錄發送到鏡像服務器實例,鏡像服務器實例將傳入的日誌記錄應用於鏡像數據庫,從而將其繼續前滾。 有關詳細信息,請參閱 數據庫鏡像。
Transaction Log characteristics
SQL Server 數據庫引擎 事務日誌的特徵:
- 事務日誌是做爲數據庫中的單獨的文件或一組文件實現的。 日誌緩存與數據頁的緩衝區高速緩存是分開管理的,所以可在數據庫引擎中生成簡單、快速和功能強大的代碼。 有關詳細信息,請參閱事務日誌物理體系結構。
- 日誌記錄和頁的格式沒必要遵照數據頁的格式。
- 事務日誌能夠在幾個文件上實現。 經過設置日誌的 FILEGROWTH 值能夠將這些文件定義爲自動擴展。 這樣可減小事務日誌內空間不足的可能性,同時減小管理開銷。 有關詳細信息,請參閱 ALTER DATABASE (Transact-SQL)。
- 重用日誌文件中空間的機制速度快且對事務吞吐量影響最小。
事務日誌截斷
日誌截斷將釋放日誌文件的空間,以便由事務日誌從新使用。 必須按期截斷事務日誌,防止其佔滿分配的空間(絕對會!)。 幾個因素可能延遲日誌截斷,所以監視日誌大小很重要。 某些操做能夠最小日誌量進行記錄以減小其對事務日誌大小的影響。
日誌截斷可從 SQL Server 數據庫的邏輯事務日誌中刪除不活動的虛擬日誌文件,釋放邏輯日誌中的空間以便物理事務日誌重用這些空間。 若是事務日誌從不截斷,它最終將填滿分配給物理日誌文件的全部磁盤空間。
爲了不空間不足,除非因爲某些緣由延遲日誌截斷,不然將在如下事件後自動進行截斷:
-
簡單恢復模式下,在檢查點以後發生。
-
在完整恢復模式或大容量日誌恢復模式下,若是自上一次備份後生成檢查點,則在日誌備份後進行截斷(除非是僅複製日誌備份)。
有關詳細信息,請參閱本主題後面的 可能延遲日誌截斷的因素。
備註
日誌截斷並不減少物理日誌文件的大小。 若要減小物理日誌文件的物理大小,則必須收縮日誌文件。 有關收縮物理日誌文件大小的信息,請參閱 Manage the Size of the Transaction Log File。
Factors that can delay log truncation
在日誌記錄長時間處於活動狀態時,事務日誌截斷將延遲,事務日誌可能填滿,這一點咱們在本主題(很長)前面提到過。
[!IMPORTANT} 有關如何響應已滿事務日誌的信息,請參閱解決事務日誌已滿的問題(SQL Server 錯誤 9002)。
實際上,日誌截斷會因爲多種緣由發生延遲。 查詢 sys.databases 目錄視圖的 log_reuse_wait 和 log_reuse_wait_desc 列,瞭解哪些因素(若是存在)阻止日誌截斷。 下表對這些列的值進行了說明。
log_reuse_wait 值 log_reuse_wait_desc 值 說明 0 NOTHING 當前有一個或多個可重複使用的虛擬日誌文件。 1 CHECKPOINT 自上第二天志截斷以後,還沒有生成檢查點,或者日誌頭還沒有跨一個虛擬日誌文件移動。 (全部恢復模式)
這是日誌截斷延遲的常見緣由。 有關詳細信息,請參閱數據庫檢查點 (SQL Server)。2 LOG_BACKUP 在截斷事務日誌前,須要進行日誌備份。 (僅限完整恢復模式或大容量日誌恢復模式)
完成下一個日誌備份後,一些日誌空間可能變爲可重複使用。3 ACTIVE_BACKUP_OR_RESTORE 數據備份或還原正在進行(全部恢復模式)。
若是數據備份阻止了日誌截斷,則取消備份操做可能有助於解決備份直接致使的此問題。4 ACTIVE_TRANSACTION 事務處於活動狀態(全部恢復模式):
一個長時間運行的事務可能存在於日誌備份的開頭。 在這種狀況下,可能須要進行另外一個日誌備份才能釋放空間。 請注意,長時間運行的事務將阻止全部恢復模式下的日誌截斷,包括簡單恢復模式,在該模式下事務日誌通常在每一個自動檢查點截斷。
延遲事務。 「延遲的事務 」是有效的活動事務,由於某些資源不可用,其回滾受阻。 有關致使事務延遲的緣由以及如何使它們擺脫延遲狀態的信息,請參閱延遲的事務 (SQL Server)。
長時間運行的事務也可能會填滿 tempdb 的事務日誌。 Tempdb 由用戶事務隱式用於內部對象,例如用於排序的工做表、用於哈希的工做文件、遊標工做表,以及行版本控制。 即便用戶事務只包括讀取數據(SELECT
查詢),也可能會以用戶事務的名義建立和使用內部對象, 而後就會填充 tempdb 事務日誌。5 DATABASE_MIRRORING 數據庫鏡像暫停,或者在高性能模式下,鏡像數據庫明顯滯後於主體數據庫。 (僅限完整恢復模式)
有關詳細信息,請參閱數據庫鏡像 (SQL Server)。6 REPLICATION 在事務複製過程當中,與發佈相關的事務仍未傳遞到分發數據庫。 (僅限完整恢復模式)
有關事務複製的信息,請參閱 SQL Server Replication。7 DATABASE_SNAPSHOT_CREATION 正在建立數據庫快照。 (全部恢復模式)
這是日誌截斷延遲的常見緣由,一般也是主要緣由。8 LOG_SCAN 發生日誌掃描。 (全部恢復模式)
這是日誌截斷延遲的常見緣由,一般也是主要緣由。9 AVAILABILITY_REPLICA 可用性組的輔助副本正將此數據庫的事務日誌記錄應用到相應的輔助數據庫。 (完整恢復模式)
有關詳細信息,請參閱:AlwaysOn 可用性組概述 (SQL Server)。10 — 僅供內部使用 11 — 僅供內部使用 12 — 僅供內部使用 13 OLDEST_PAGE 若是將數據庫配置爲使用間接檢查點,數據庫中最先的頁可能比檢查點 LSN 早。 在這種狀況下,最先的頁能夠延遲日誌截斷。 (全部恢復模式)
有關間接檢查點的信息,請參閱數據庫檢查點 (SQL Server)。14 OTHER_TRANSIENT 當前未使用此值。 可儘可能減小日誌量的操做
最小日誌記錄是指只記錄在不支持時間點恢復的狀況下恢復事務所需的信息。 本主題介紹在大容量日誌恢復模式下(以及簡單恢復模式下)按最小方式記錄、但在運行備份時例外的操做。
備註
內存優化表不支持最小日誌記錄。
備註
在完整 恢復模式下,全部大容量操做都將被完整地記錄下來。 可是,能夠經過將數據庫暫時切換到用於大容量操做的大容量日誌恢復模式,最小化一組大容量操做的日誌記錄。 最小日誌記錄比完整日誌記錄更爲有效,並在大容量事務期間,下降了大規模大容量操做填滿可用的事務日誌空間的可能性。 不過,若是在最小日誌記錄生效時數據庫損壞或丟失,則沒法將數據庫恢復到故障點。
下列操做在完整恢復模式下執行完整日誌記錄,而在簡單和大容量日誌恢復模式下按最小方式記錄日誌:
- 批量導入操做(bcp、BULK INSERT 和 INSERT...SELECT)。 有關在什麼時候對大容量導入表按最小方式進行記錄的詳細信息,請參閱 Prerequisites for Minimal Logging in Bulk Import。
啓用事務複製時,將徹底記錄 BULK INSERT 操做,即便處於大容量日誌恢復模式下。
- SELECT INTO 操做。
啓用事務複製時,將徹底記錄 SELECT INTO 操做,即便處於大容量日誌恢復模式下。
-
插入或追加新數據時,使用 UPDATE 語句中的 .WRITE 子句部分更新到大型值數據類型。 注意,在更新現有值時沒有使用最小日誌記錄。有關大型值數據類型的詳細信息,請參閱數據類型 (Transact-SQL)。
-
在UPDATETEXT 、 nUPDATETEXT 和 UPDATETEXT, nUPDATETEXT, 、 UPDATETEXT 語句。 注意,在更新現有值時沒有使用最小日誌記錄。
重要
不推薦使用 WRITETEXT 語句和 UPDATETEXT 語句,應該避免在新的應用程序中使用這些語句。
-
若是數據庫設置爲簡單或大容量日誌恢復模式,則不管是脫機仍是聯機執行操做,都會按最小方式記錄一些索引 DDL 操做。 按最小方式記錄的索引操做以下:
-
CREATE INDEX 操做(包括索引視圖)。
-
ALTER INDEX REBUILD 或 DBCC DBREINDEX 操做。
重要
不推薦使用「DBCC DBREINDEX 語句」;請不要在新的應用程序中使用該語句。
-
DROP INDEX 新堆從新生成(若是適用)。 ( DROP INDEX 操做期間將 始終 完整記錄索引頁的釋放操做。)
-
-
SQL Server 鎖詳解
鎖是一種防止在某對象執行動做的一個進程與已在該對象上執行的其餘進行相沖突的機制。也就是說,若是有其餘人在操做某個對象,那麼你舊不能在該對象上進行操做。你可否執行操做取決於其餘用戶正在進行的操做。
經過鎖能夠防止的問題
鎖能夠解決如下4種主要問題:
- 髒讀
- 非重複性讀取
- 幻讀
- 丟失更新
一、髒讀
若是一個事務讀取的記錄是另外一個未完成事務的一部分,那麼這時就發生了髒讀。若是第一個事務正常完成,那麼就有什麼問題。可是,若是前一個事務回滾了呢,那將從數據庫從未發生的事務中獲取了信息。
二、非重複性讀取
很容易將非重複性讀取和髒讀混淆。若是一個事務中兩次讀取記錄,而另外一個事務在這期間改變了數據,就會發生非重複性讀取。
例如,一個銀行帳戶的餘額是不容許小於0的。若是一個事務讀取了某帳戶的餘額爲125元,此時另外一事務也讀取了125元,若是兩個事務都扣費100元,那麼這時數據庫的餘額就變成了-75元。有兩種方式能夠防止這個問題:
- 建立CHECK約束並監控547錯誤
- 將隔離級別設置爲REPEATABLEREAD或SERIALIZABLE
CHECK約束看上去至關直觀。要知道的是,這是一種被動的而非主動的方法。然而,在不少狀況下可能須要使用非重複性讀取,因此這在不少狀況下是首選。
三、幻讀
幻讀發生的機率很是小,只有在很是偶然的狀況下才會發生。
好比,你想將一張工資表裏全部低於100的人的工資,提升到100元。你可能會執行如下SQL語句:
UPDATE tb_Money SET Salary = 100 WHERE Salary < 100
這樣的語句,一般狀況下,沒有問題。可是若是,你在UPDATE的過程當中,有人剛好有INSERT了一條工資低於100的數據,由於這是一個全新的數據航,因此沒有被鎖定,並且它會被漏過Update。
要解決這個問題,惟一的方法是設定事務隔離級別爲SERIALIZABLE,在這種狀況下,任何對錶的更新都不能放入WHERE子句中,不然他們將被鎖在外面。
四、丟失更新
丟失更新發生在一個更新成功寫入數據庫後,而又意外地被另外一個事務重寫時。這是怎麼發生的呢?若是有兩個事務讀取整個記錄,而後其中一個向記錄寫入了更新信息,而另外一個事務也向該記錄寫入更新信息,這是就會出現丟失更新。
有個例子寫得很好,這裏照敲下來吧。假如你是公司的一位信用分析員,你接到客戶X打開的電話,說他已達到它的信用額度上限,想申請增長額度,因此你查看了這位客戶的信息,你發現他的信用額度是5000,而且看他每次都能按時付款。
當你在查看的時候,信用部門的另外一位員工也讀取了客戶X的記錄,並輸入信息改變了客戶的地址。它讀取的記錄也顯示信用額度爲5000。
這時你決定把客戶X的信用額度提升到10000,而且按下了Enter鍵,數據庫如今顯示客戶X的信用額度爲10000。
Sally如今也更新了客戶X的地址,可是她仍然使用和您同樣的編輯屏幕,也就是說,她更新了整個記錄。還記得她屏幕上顯示的信用額度嗎?是5000.數據庫如今又一次顯示客戶X的信用額度爲5000。你的更新丟失了。
解決這個問題的方法取決於你讀取某數據和要更新數據的這段時間內,代碼以何種方式識別出另外一鏈接已經更新了該記錄。這個識別的方式取決於你所使用的訪問方法。
能夠鎖定的資源
對於SQL Server來講,有6種可鎖定的資源,並且它們造成了一個層次結構。鎖的層次越高,它的粒度就越粗。按粒度由粗到細排列,這些資源包括:
- 數據庫:鎖定整個數據庫。這一般發生在整個數據庫模式改變的時候。
- 表:鎖定整個表。這包含了與該表相關聯的全部數據相關的對象,包括實際的數據行(每一行)以及與該表相關聯的全部索引中的鍵。
- 區段:鎖定整個區段。由於一個區段由8頁組成,因此區段鎖定是指鎖定控制了區段、控制了該區段內8個數據或索引頁以及這8頁中的全部數據航。
- 頁:鎖定該頁中的全部數據或索引鍵。
- 鍵:在索引中的特定鍵或一系間上有鎖。相同索引頁中的其餘鍵不受影響。
- 行或行標識符:雖然從技術上將,鎖是放在行標識符上的,可是本質上,它鎖定了整個數據行。
鎖升級和鎖對性能的影響
升級是指可以認識到維持一個較細的粒度(例如,行鎖而不是頁鎖),只在被鎖定的項數較少時有意義。而隨着愈來愈多的項目被鎖定,維護這些鎖的系統開銷實際上會影響性能。這會致使所持續更長的時間。
當維持鎖的數量達到必定限度時,則鎖升級爲下一個更高的層次,而且將不須要再如此緊密地管理低層次的鎖(釋放資源,並且有助於提高速度)。
注意,升級是基於鎖的數量,而不是用戶的數量。這裏的重點是,能夠經過執行大量的更新來單獨地鎖定表-行鎖能夠升級爲頁鎖,頁鎖能夠升級爲表鎖。這意味着可能將全部其餘用戶鎖在該表以外。若是查詢使用了多個表,則它極可能將每一個人鎖在這些表以外。
鎖定模式
除了須要考慮鎖定的資源層次之外,還要考慮查詢將要得到的鎖定模式,就像須要對不一樣的資源進行鎖定同樣,也有不一樣的鎖定模式。一些模式是互相排斥的。一些模式什麼都不作,只修改其餘的模式。模式是否能夠一塊兒使用取決於他們是不是兼容的。
一、共享鎖
這是最基本的一種鎖。共享鎖用於只須要讀取數據的時候,也就是說,共享鎖鎖定時,不會進行改變內容的操做,其餘用戶容許讀取。
共享鎖能和其餘共享鎖兼容。雖然共享鎖不介意其餘鎖的存在,可是有些鎖並不能和共享鎖共存。
共享鎖告訴其餘鎖,某用戶已經在那邊了,它們並不提供不少的功能,可是不能忽略它們。然而,共享鎖能作的是防止用戶執行髒讀。
二、排它鎖
排它鎖顧名思義,排它鎖不與其餘任何鎖兼容。若是有任何其餘其餘鎖存在,則不能使用排他鎖,並且當排他鎖仍然起做用時,他們不容許在資源之上建立任何形式的新鎖。這能夠防止兩我的同時更新、刪除或執行任何操做。
三、更新鎖
更新鎖是共享鎖和排他鎖的混合。更新鎖是一種特殊的佔位符。爲了能執行UPDATE,須要驗證WHERE子句來指出想要更新的具體的數據行。這意味着只須要一個共享鎖,直到真正地進行物理更新。在物理更新期間,須要一個排他鎖。
- 第一個階段指出了知足WHERE子句條件的內容,這是更新查詢的一部分,該查詢有一個更新鎖。
- 第二個階段是若是決定執行更新,那麼鎖將升級爲排他鎖。不然,將把鎖轉換爲共享鎖。
這樣作的好處是它防止了死鎖。死鎖自己不是一種鎖定類型,而是一種已經造成矛盾的情況,兩個鎖在互相等待,多個鎖造成一個環在等待前面的事務清除資源。
若是沒有更新鎖,死鎖會一直出現。兩個更新查詢會在共享模式下運行。Query A完成了它的查詢工做並準備進行物理更新。它想升級爲排他鎖,可是不能夠這麼作,由於Query B正在完成查詢。除非Query B須要進行物理更新,不然它會完成查詢。爲了作到這點,Query B必須升級爲排他鎖,可是不能這麼作,由於Query A正在等待。這樣就形成了僵局。
而更新鎖阻止創建其餘的更新鎖。第二個事務只要嘗試取得一個更新鎖,它們就會進入等待狀態,直到超時爲止-將不會授予這個鎖。若是第一個鎖在鎖超時以前清除的話,則鎖定會授予給新的請求者,而且這個處理會繼續下去。若是不清楚,則會發生錯誤。
更新鎖只與共享鎖以及意向共享鎖相兼容。
四、意向鎖
意向鎖是什麼意思呢?就是說,加入你鎖定了某一行,那麼同時也加了表的意向鎖(不容許其餘人經過表鎖來妨礙你)。
意向鎖是真正的佔位符,它用來處理對象層次問題的。假設一下以下狀況:已對某一行創建了鎖,可是有人想在頁上或區段上創建所,或者是修改表。你確定不想讓另外一個事務經過達到更高的層次來妨礙你。
若是沒有意向鎖,那麼較高層次的對象將不會知道在較低層次上有鎖。意向鎖可改進性能,由於SQL Server只須要在表層次上檢查意向鎖(而不須要檢查表上的每一個行鎖或者頁鎖),以此來決定事務是否能夠安全地鎖定整個表。意向鎖分爲如下3種不一樣的類型:
- 意向共享鎖:該意向鎖指已經或者將要在層次結構的一些較低點處創建共享鎖。
- 意向排他鎖:它與意向共享鎖同樣,可是將會在低層項上設置排他鎖。
- 共享意向排他鎖:它指已經或將會在對象層次結構下面創建共享鎖,但目的是爲了修改數據,因此它會在某個時刻成爲意向排它鎖。
五、模式鎖
模式鎖分爲如下兩種。
- 模式修改鎖:對對象進行模式改變。在Sch-M鎖期間,不能對對象進行查詢或其餘CREATE、ALTER或DROP語句的操做。
- 模式穩定鎖鎖定:它和共享鎖很類似;這個鎖的惟一目的是方式模式修改鎖,由於在該對象上已有其餘查詢(或CREATE、ALTER、DROP語句)的鎖。它與其餘全部的鎖定相兼容。
六、批量更新鎖
批量更新鎖(BU)只是一種略有不一樣的表鎖定變體形式。批量更新鎖容許並行加載數據。也就是說,對於其餘任何普通操做(T-SQL)都會將表鎖定,但能夠同時執行多個BULK INSERT或bcp操做。
鎖的兼容性
鎖的資源鎖定模式的兼容性表格,現有鎖以列顯示,要兼容的鎖以行顯示。
鎖的類型 意向共享鎖(IS) 共享鎖(S) 更新鎖(U) 意向排他鎖(IX) 共享意向排它鎖(SIX) 排他鎖(X) 意向共享鎖(IS) 是 是 是 是 是 否 共享鎖(S) 是 是 是 否 否 否 更新鎖(U) 是 是 否 否 否 否 意向排他鎖(IX) 是 否 否 是 否 否 共享意向排它鎖(SIX) 是 否 否 否 否 否 排他鎖(X) 否 否 否 否 否 否 另外:
- Sch-S與出Sch-M之外的全部鎖定模式相兼容。
- Sch-M和全部的鎖定模式不兼容。
- BU只與模式穩定性鎖以及其餘的批量更新鎖相兼容。
有時想要在查詢中或在整個事務中對鎖定有更多的控制。能夠經過使用優化器提示(optimizer hints)來實現這一點。
優化器提示明確告訴SQL Server將一個鎖升級爲特有的層次。這些提示信息包含在將要影響的表的名稱以後。
優化器提示是一個高級主題,有經驗的SQL Server開發人員會常用它,而且他們至關重視它。
使用Management Studio肯定鎖
查看鎖的最好方式是使用Management Studio。經過使用Activity Monitor,Management Studio會以兩種方式顯示鎖-經過processId或object。
爲了使用Management Studio顯示鎖,只要導航到<Server>的Activity Monitor節點,其中的<Server>是監控其活動的服務器的頂級節點。
展開感興趣的節點(Overview部分默認展開),能夠經過滾動條查看大量度量值-包括當前系統中有效的鎖。
顯示界面以下:
設置隔離級別
事務和鎖之間的聯繫是很緊密的。默認狀況下,一旦建立了任何與數據修改相關的鎖,該鎖定就會在整個事務期間存在。若是有一個大型事務,就意味着將在很長一段時間內阻止其餘進程訪問鎖定的對象。這明顯是有問題的。
事務有5種隔離級別:
- READ COMMITTED
- READ UNCOMMITTED
- REPEATABLE READ
- SERIALIZABLE
- SNAPSHOT
在這些隔離級別之間進行切換的語法也至關直觀:
SET TRANSACTION ISOLATION LEVEL < READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE | SNAPSHOT >
對隔離級別的修改只會影響到當前的鏈接-因此沒必要擔憂會影響到其餘的用戶。其餘用戶也影響不了你。
一、READ COMMITTED
默認狀況就是這個,經過READ COMMITTED,任何建立的共享鎖將在建立它們的語句完成後自動釋放。也就是說,若是啓動了一個事務,運行了一些語句,而後運行SELECT語句,再運行一些其餘的語句,那麼當SELECT語句完成的時候,與SELECT語句相關聯的鎖就會釋放 - SQL Server並不會等到事務結束。
動做查詢(UPDATE、DELETE、INSERT)有點不一樣。若是事務執行了修改數據的查詢,則這些鎖將會在事務期間保持有效。
經過設置READ COMMITTED這一默認隔離級別,能夠肯定有足夠的數據完整性來防止髒讀。然而,仍會發生非重複性讀取和幻讀。
二、READ UNCOMMITTED
READ UNCOMMITTED是全部隔離級別中最危險的,可是它在速度方面有最好的性能。
設置隔離級別爲READ UNCOMMITTED將告訴SQL Server不要設置任何鎖,也不要事先任何鎖。
鎖既是你的保護者,同時也是你的敵人。鎖能夠防止數據完整性問題,可是鎖也常常妨礙或阻止你訪問須要的數據。因爲此鎖存在髒讀的危險,所以此鎖只能應用於並不是十分精確的環境中。三、REPEATABLE READ
REPEATABLE READ會稍微地將隔離級別升級,並提供一個額外的併發保護層,這不只能防止髒讀,並且能防止非重複性讀取。
防止非重複性讀取是很大的優點,可是直到事務結束還保持共享鎖會阻止用戶訪問對象,所以會影響效率。推薦使用其餘的數據完整性選項,例如CHECK約束,而不是採用這個選擇。
與REPEATABLE READ隔離級別等價的優化器提示是REPEATABLEREAD(除了一個空格,二者並沒有不一樣)。四、SERIALIZABLE
SERIALIZABLE是堡壘級的隔離級別。除了丟失更新之外,它防止全部形式的併發問題。甚至能防止幻讀。
若是設置隔離級別爲SERIALIZABLE,就意味着對事物使用的表進行的任何UPDATE、DELETE、INSERT操做絕對不知足該事務中任何語句的WHERE子句的條件。從本質上說,若是用戶想執行一些事務感興趣的事情,那麼必須等到該事務完成的時候。
SERIALIZABLE隔離級別也能夠經過查詢中使用SERIALIZABLE或HOLDLOCK優化器提示模擬。再次申明,相似於READ UNCOMMITTED和NOLOCK,前者不須要每次都設置,然後者須要把隔離級別設置回來。
五、SNAPSHOT
SNAPSHOT是最新的一種隔離級別,很是想READ COMMITTED和READ UNCOMMITTED的組合。要注意的是,SNAPSHOT默認是不可用的-只有爲數據庫打開了ALLOW_SNAPSHOT_ISOLATION特殊選項時,SNAPSHOT纔可用。
和READ UNCOMMITED同樣,SNAPSHOT並不建立任何鎖,也不實現人和所。二者的主要區別是它們識別數據庫中不一樣時段發生的更改。數據庫中的更改,無論什麼時候或是否提交,都會被運行READ UNCOMMITTED隔離級別的查詢看到。而使用SNAPSHOT,只能看到在SNAPSHOT事務開始以前提交的更改。從SNAPSHOT事務一開始執行,全部查看到的數據就和在時間開始時提交的同樣。處理死鎖
死鎖的錯誤號是1205。
若是一個鎖因爲另外一個鎖佔有資源而不能完成應該作的清除資源工做,就會致使死鎖;反之亦然。當發生死鎖時,須要其中的一方贏得這場鬥爭,因此SQL Server選擇一個死鎖犧牲者,對死鎖犧牲者的事務進行回滾,而且經過1205錯誤來通知發生了死鎖。另一個事務將繼續正常地運行。
一、判斷死鎖的方式
每隔5秒鐘,SQL Server就會檢查全部當前的事務,瞭解他們在等待什麼還未被授予的鎖。而後再一次從新檢查全部打開的鎖請求的狀態,若是先前請求中有一個尚未被授予,則它會遞歸地檢查全部打開的事務,尋找鎖定請求的循環鏈。若是SQL Server找到這樣的村換連,則將會選擇一個或更多的死鎖犧牲者。
二、選擇死鎖犧牲者的方式
默認狀況下,基於相關事務的"代價",選擇死鎖犧牲者。SQL Server將會選擇回滾代價最低的事務。在必定程度上,能夠使用SQL Server中的DEADLOCK_PRIORITY SET選項來重寫它。
三、避免死鎖
避免死鎖的經常使用規則
-
按相同的順序使用對象
- 使事務儘量簡短而且在一個批處理中。
- 儘量使用最低的事務隔離級別。
- 在同一事務中不容許無限度的中斷。
- 在控制環境中,使用綁定鏈接。
一、按相同的順序使用對象
例若有兩個表:Suppliers和Products。假設有兩個進程將使用這兩個表。進程1接受庫存輸入,用手頭新的產品總量更新Products表,接下來用已經購買的產品總量來更新Suppliers表。進程2記錄銷售數據,它在Supperlier表中更新銷售產品的總量,而後在Product中減小庫存數量。
若是同時運行這兩個進程,那麼就有可能碰到麻煩。進程1試圖獲取Product表上的一個排他鎖。進程2將在Suppliers表上獲取一個排他鎖。而後進程1將試圖獲取Suppliers表上的一個鎖,可是進程1必須等到進程2清除了現有的鎖。同時進程2也在等待進程1清除現有鎖。
上面的例子是,兩個進程用相反的順序,鎖住兩個表,這樣就很容易發生死鎖。
若是咱們將進程2改爲首先在Products減小庫存數量,接着在Suppliers表中更新銷售產品的總數量。兩個進程以相同的順序訪問兩張表,這樣就可以減小死鎖的發生。
二、使事務儘量簡短
保持事務的簡短將不會讓你付出任何的代價。在事務中放入想要的內容,把不須要的內容拿出來,就這麼簡單。它的原理並不複雜-事務打開的時間越長,它觸及的內容就越多,那麼其餘一些進程想要獲得正在使用的一個或者多個對象的可能性就越大。若是要保持事務簡短,那麼就將最小化可能引發死鎖的對象的數量,還將減小鎖定對象的時間。原理就如此簡單。
三、儘量使用最低的事務隔離級別
使用較低的隔離級別和較高的隔離級別相比,共享鎖持續的時間更短,所以會減小鎖的競爭。
四、不要採用容許無限中斷的事務
當開始執行某種開放式進程時間,不要建立將一直佔有資源的鎖。一般,指的是用戶交互,但它也多是容許無限等待的任何進程。
-
軟件架構之 23種設計模式
建立型模式
一、FACTORY—追MM少不了請吃飯了,麥當勞的雞翅和肯德基的雞翅都是MM愛吃的東西,雖然口味有所不一樣,但無論你帶MM去麥當勞或肯德基,只管向服務員說「來四個雞翅」就好了。麥當勞和肯德基就是生產雞翅的Factory
工廠模式:客戶類和工廠類分開。消費者任什麼時候候須要某種產品,只需向工廠請求便可。消費者無須修改就能夠接納新產品。缺點是當產品修改時,工廠類也要作相應的修改。如:如何建立及如何向客戶端提供。
二、 BUILDER—MM最愛聽的就是「我愛你」這句話了,見到不一樣地方的MM,要可以用她們的方言跟她說這句話哦,我有一個多種語言翻譯機,上面每種語言都 有一個按鍵,見到MM我只要按對應的鍵,它就可以用相應的語言說出「我愛你」這句話了,國外的MM也能夠輕鬆搞掂,這就是個人「我愛你」builder。 (這必定比美軍在伊拉克用的翻譯機好賣)
建造模式:將產品的內部表象和產品的生成過程分割開來,從而使一個建造過程生成具備不一樣的內部表象的產品對象。建造模式使得產品內部表象能夠獨立的變化,客戶沒必要知道產品內部組成的細節。建造模式能夠強制實行一種分步驟進行的建造過程。
三、FACTORY METHOD—請MM去麥當勞吃漢堡,不一樣的MM有不一樣的口味,要每一個都記住是一件煩人的事情,我通常採用Factory Method模式,帶着MM到服務員那兒,說「要一個漢堡」,具體要什麼樣的漢堡呢,讓MM直接跟服務員說就好了。
工廠方法模式:核心工廠類再也不負責全部產品的建立,而是將具體建立的工做交給子類去作,成爲一個抽象工廠角色,僅負責給出具體工廠類必須實現的接口,而不接觸哪個產品類應當被實例化這種細節。
四、PROTOTYPE—跟MM用QQ聊天,必定要說些深情的話語了,我搜集了好多肉麻的情話,須要時只要copy出來放到QQ裏面就好了,這就是個人情話prototype了。(100塊錢一份,你要不要)
原 始模型模式:經過給出一個原型對象來指明所要建立的對象的類型,而後用複製這個原型對象的方法建立出更多同類型的對象。原始模型模式容許動態的增長或減小 產品類,產品類不須要非得有任何事先肯定的等級結構,原始模型模式適用於任何的等級結構。缺點是每個類都必須配備一個克隆方法。
五、SINGLETON—俺有6個漂亮的老婆,她們的老公都是我,我就是咱們家裏的老公Sigleton,她們只要說道「老公」,都是指的同一我的,那就是我(剛纔作了個夢啦,哪有這麼好的事)
單例模式:單例模式確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例單例模式。單例模式只應在有真正的「單一實例」的需求時纔可以使用。
結構型模式
六、ADAPTER—在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助於個人朋友kent了,他做爲我和Sarah之間的Adapter,讓我和Sarah能夠相互交談了(也不知道他會不會耍我)
適配器(變壓器)模式:把一個類的接口變換成客戶端所期待的另外一種接口,從而使本來因接口緣由不匹配而沒法一塊兒工做的兩個類可以一塊兒工做。適配類能夠根據參數返還一個合適的實例給客戶端。
七、BRIDGE—早上碰到MM,要說早上好,晚上碰到MM,要說晚上好;碰到MM穿了件新衣服,要說你的衣服好漂亮哦,碰到MM新作的髮型,要說你的頭髮好漂亮哦。不要問我「早上碰到MM新作了個髮型怎麼說」這種問題,本身用BRIDGE組合一下不就好了
橋樑模式:將抽象化與實現化脫耦,使得兩者能夠獨立的變化,也就是說將他們之間的強關聯變成弱關聯,也就是指在一個軟件系統的抽象化和實現化之間使用組合/聚合關係而不是繼承關係,從而使二者能夠獨立的變化。
八、 COMPOSITE—Mary今天過生日。「我過生日,你要送我一件禮物。」「嗯,好吧,去商店,你本身挑。」「這件T恤挺漂亮,買,這條裙子好看,買, 這個包也不錯,買。」「喂,買了三件了呀,我只答應送一件禮物的哦。」「什麼呀,T恤加裙子加包包,正好配成一套呀,小姐,麻煩你包起 來。」「……」,MM都會用Composite模式了,你會了沒有?
合成模式:合成模式將對象組織到樹結構中,能夠用來描述總體與部分的關係。合成模式就是一個處理對象的樹結構的模式。合成模式把部分與總體的關係用樹結構表示出來。合成模式使得客戶端把一個個單獨的成分對象和由他們複合而成的合成對象同等看待。
九、 DECORATOR—Mary過完輪到Sarly過生日,仍是不要叫她本身挑了,否則這個月伙食費確定玩完,拿出我去年在華山頂上照的照片,在背面寫上 「最好的的禮物,就是愛你的Fita」,再到街上禮品店買了個像框(賣禮品的MM也很漂亮哦),再找隔壁搞美術設計的Mike設計了一個漂亮的盒子裝起 來……,咱們都是Decorator,最終都在修飾我這我的呀,怎麼樣,看懂了嗎?
裝飾模式:裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案,提供比繼承更多的靈活性。動態給一個對象增長功能,這些功能能夠再動態的撤消。增長由一些基本功能的排列組合而產生的很是大量的功能。
十、FACADE—我有一個專業的Nikon相機,我就喜歡本身手動調光圈、快門,這樣照出來的照片才專業,但MM可不懂這些,教了半天也不會。幸虧相機有Facade設計模式 ,把相機調整到自動檔,只要對準目標按快門就好了,一切由相機自動調整,這樣MM也能夠用這個相機給我拍張照片了。
門面模式:外部與一個子系統的通訊必須經過一個統一的門面對象進行。門面模式提供一個高層次的接口,使得子系統更易於使用。每個子系統只有一個門面類,並且此門面類只有一個實例,也就是說它是一個單例模式。但整個系統能夠有多個門面類。
十一、 FLYWEIGHT—天天跟MM發短信,手指都累死了,最近買了個新手機,能夠把一些經常使用的句子存在手機裏,要用的時候,直接拿出來,在前面加上MM的名 字就能夠發送了,再不用一個字一個字敲了。共享的句子就是Flyweight,MM的名字就是提取出來的外部特徵,根據上下文狀況使用。
享 元模式:FLYWEIGHT在拳擊比賽中指最輕量級。享元模式以共享的方式高效的支持大量的細粒度對象。享元模式能作到共享的關鍵是區份內蘊狀態和外蘊狀 態。內蘊狀態存儲在享元內部,不會隨環境的改變而有所不一樣。外蘊狀態是隨環境的改變而改變的。外蘊狀態不能影響內蘊狀態,它們是相互獨立的。將能夠共享的 狀態和不能夠共享的狀態從常規類中區分開來,將不能夠共享的狀態從類裏剔除出去。客戶端不能夠直接建立被共享的對象,而應當使用一個工廠對象負責建立被共 享的對象。享元模式大幅度的下降內存中對象的數量。
十二、PROXY—跟MM在網上聊天,一開頭老是「hi,你好」,「你從哪兒來呀?」「你多大了?」「身高多少呀?」這些話,真煩人,寫個程序作爲個人Proxy吧,凡是接收到這些話都設置好了自動的回答,接收到其餘的話時再通知我回答,怎麼樣,酷吧。
代 理模式:代理模式給某一個對象提供一個代理對象,並由代理對象控制對源對象的引用。代理就是一我的或一個機構表明另外一我的或者一個機構採起行動。某些狀況 下,客戶不想或者不可以直接引用一個對象,代理對象能夠在客戶和目標對象直接起到中介的做用。客戶端分辨不出代理主題對象與真實主題對象。代理模式能夠並 不知道真正的被代理對象,而僅僅持有一個被代理對象的接口,這時候代理對象不可以建立被代理對象,被代理對象必須有系統的其餘角色代爲建立並傳入。
行爲模式
1三、 CHAIN OF RESPONSIBLEITY—晚上去上英語課,爲了好開溜坐到了最後一排,哇,前面坐了好幾個漂亮的MM哎,找張紙條,寫上「Hi,能夠作個人女友 嗎?若是不肯意請向前傳」,紙條就一個接一個的傳上去了,糟糕,傳到第一排的MM把紙條傳給老師了,據說是個老處女呀,快跑!
責任鏈模式:在責任鏈模式中,不少對象由每個對象對其下家的引用而接
起 來造成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。客戶並不知道鏈上的哪個對象最終處理這個請求,系統能夠在不影響客戶端的狀況 下動態的從新組織鏈和分配責任。處理者有兩個選擇:承擔責任或者把責任推給下家。一個請求能夠最終不被任何接收端對象所接受。
1四、 COMMAND—俺有一個MM家裏管得特別嚴,無法見面,只好藉助於她弟弟在咱們倆之間傳送信息,她對我有什麼指示,就寫一張紙條讓她弟弟帶給我。這不, 她弟弟又傳送過來一個COMMAND,爲了感謝他,我請他吃了碗雜醬麪,哪知道他說:「我同時給我姐姐三個男友送COMMAND,就數你最小氣,才請我 吃麪。」,:-(
命 令模式:命令模式把一個請求或者操做封裝到一個對象中。命令模式把發出命令的責任和執行命令的責任分割開,委派給不一樣的對象。命令模式容許請求的一方和發 送的一方獨立開來,使得請求的一方沒必要知道接收請求的一方的接口,更沒必要知道請求是怎麼被接收,以及操做是否執行,什麼時候被執行以及是怎麼被執行的。系統支 持命令的撤消。
1五、INTERPRETER—俺有一個《泡MM真經》,上面有各類泡MM的攻略,好比說去吃西餐的步驟、去看電影的方法等等,跟MM約會時,只要作一個Interpreter,照着上面的腳本執行就能夠了。
解 釋器模式:給定一個語言後,解釋器模式能夠定義出其文法的一種表示,並同時提供一個解釋器。客戶端能夠使用這個解釋器來解釋這個語言中的句子。解釋器模式 將描述怎樣在有了一個簡單的文法後,使用模式設計解釋這些語句。在解釋器模式裏面提到的語言是指任何解釋器對象可以解釋的任何組合。在解釋器模式中須要定 義一個表明文法的命令類的等級結構,也就是一系列的組合規則。每個命令對象都有一個解釋方法,表明對命令對象的解釋。命令對象的等級結構中的對象的任何 排列組合都是一個語言。
1六、ITERATOR—我愛上了Mary,不顧一切的向她求婚。
Mary:「想要我跟你結婚,得答應個人條件」
我:「什麼條件我都答應,你說吧」
Mary:「我看上了那個一克拉的鑽石」
我:「我買,我買,還有嗎?」
Mary:「我看上了湖邊的那棟別墅」
我:「我買,我買,還有嗎?」
Mary:「你的小弟弟必需要有50cm長」
我腦殼嗡的一聲,坐在椅子上,一咬牙:「我剪,我剪,還有嗎?」
……
迭 代子模式:迭代子模式能夠順序訪問一個彙集中的元素而沒必要暴露彙集的內部表象。多個對象聚在一塊兒造成的整體稱之爲彙集,彙集對象是可以包容一組對象的容器 對象。迭代子模式將迭代邏輯封裝到一個獨立的子對象中,從而與彙集自己隔開。迭代子模式簡化了彙集的界面。每個彙集對象均可以有一個或一個以上的迭代子 對象,每個迭代子的迭代狀態能夠是彼此獨立的。迭代算法能夠獨立於彙集角色變化。
1七、MEDIATOR—四個MM打麻將,相互之間誰應該給誰多少錢算不清楚了,幸好當時我在旁邊,按照各自的籌碼數算錢,賺了錢的從我這裏拿,賠了錢的也付給我,一切就OK啦,俺獲得了四個MM的電話。
調 停者模式:調停者模式包裝了一系列對象相互做用的方式,使得這些對象沒必要相互明顯做用。從而使他們能夠鬆散偶合。當某些對象之間的做用發生改變時,不會立 即影響其餘的一些對象之間的做用。保證這些做用能夠彼此獨立的變化。調停者模式將多對多的相互做用轉化爲一對多的相互做用。調停者模式將對象的行爲和協做 抽象化,把對象在小尺度的行爲上與其餘對象的相互做用分開處理。
1八、MEMENTO—同時跟幾個MM聊天時,必定要記清楚剛纔跟MM說了些什麼話,否則MM發現了會不高興的哦,幸好我有個備忘錄,剛纔與哪一個MM說了什麼話我都拷貝一份放到備忘錄裏面保存,這樣能夠隨時察看之前的記錄啦。
備忘錄模式:備忘錄對象是一個用來存儲另一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捉住,並外部化,存儲起來,從而能夠在未來合適的時候把這個對象還原到存儲起來的狀態。
1九、OBSERVER—想知道我們公司最新MM情報嗎?加入公司的MM情報郵件組就好了,tom負責蒐集情報,他發現的新情報不用一個一個通知咱們,直接發佈給郵件組,咱們做爲訂閱者(觀察者)就能夠及時收到情報啦
觀察者模式:觀察者模式定義了一種一隊多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知全部觀察者對象,使他們可以自動更新本身。
20、 STATE—跟MM交往時,必定要注意她的狀態哦,在不一樣的狀態時她的行爲會有不一樣,好比你約她今天晚上去看電影,對你沒興趣的MM就會說「有事情啦」, 對你不討厭但還沒喜歡上的MM就會說「好啊,不過能夠帶上我同事麼?」,已經喜歡上你的MM就會說「幾點鐘?看完電影再去泡吧怎麼樣?」,固然你看電影過 程中表現良好的話,也能夠把MM的狀態從不討厭不喜歡變成喜歡哦。
狀 態模式:狀態模式容許一個對象在其內部狀態改變的時候改變行爲。這個對象看上去象是改變了它的類同樣。狀態模式把所研究的對象的行爲包裝在不一樣的狀態對象 裏,每個狀態對象都屬於一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行爲也隨之改變。狀態模式須要對每個系統可 能取得的狀態創立一個狀態類的子類。當系統的狀態變化時,系統便改變所選的子類。
2一、STRATEGY—跟不一樣類型的MM約會,要用不一樣的策略,有的請電影比較好,有的則去吃小吃效果不錯,有的去海邊浪漫最合適,單目的都是爲了獲得MM的芳心,個人追MM錦囊中有好多Strategy哦。
策 略模式:策略模式針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響到客戶端的狀況下 發生變化。策略模式把行爲和環境分開。環境類負責維持和查詢行爲類,各類算法在具體的策略類中提供。因爲算法和環境獨立開來,算法的增減,修改都不會影響 到環境和客戶端。
2二、 TEMPLATE METHOD——看過《如何說服女生上牀》這部經典文章嗎?女生從認識到上牀的不變的步驟分爲巧遇、打破僵局、展開追求、接吻、前戲、動手、愛撫、進去八 大步驟(Template method),但每一個步驟針對不一樣的狀況,都有不同的作法,這就要看你隨機應變啦(具體實現);
模 板方法模式:模板方法模式準備一個抽象類,將部分邏輯以具體方法以及具體構造子的形式實現,而後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不一樣的子類可 以以不一樣的方式實現這些抽象方法,從而對剩餘的邏輯有不一樣的實現。先制定一個頂級邏輯框架,而將邏輯的細節留給具體的子類去實現。
2三、 VISITOR—情人節到了,要給每一個MM送一束鮮花和一張卡片,但是每一個MM送的花都要針對她我的的特色,每張卡片也要根據我的的特色來挑,我一我的哪 搞得清楚,仍是找花店老闆和禮品店老闆作一下Visitor,讓花店老闆根據MM的特色選一束花,讓禮品店老闆也根據每一個人特色選一張卡,這樣就輕鬆多 了;
訪 問者模式:訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操做。一旦這些操做須要修改的話,接受這個操做的數據結構能夠保持不變。訪問者模式適 用於數據結構相對未定的系統,它把數據結構和做用於結構上的操做之間的耦合解脫開,使得操做集合能夠相對自由的演化。訪問者模式使得增長新的操做變的很容 易,就是增長一個新的訪問者類。訪問者模式將有關的行爲集中到一個訪問者對象中,而不是分散到一個個的節點類中。當使用訪問者模式時,要將盡量多的對象 瀏覽邏輯放在訪問者類中,而不是放到它的子類中。訪問者模式能夠跨過幾個類的等級結構訪問屬於不一樣的等級結構的成員類。
-
Oracle與Sqlserver:Order by NULL值介紹
針對頁面傳參到in的子集中去進行查詢操做的話,就會有in(xxx,null),這樣就會致使查詢的結果中其實直接過濾掉了null,根本就查不出來null的值。以前對於null的操做都是進行不一樣數據庫的null函數來進行選擇nvl、isnull、ifnull等,直接將字段的null進行轉換後再操做。
只知道要對數據庫中的null進行轉換的操做,可是不知所云,因此今天就大體瞭解下。針對oracle的null的基本操做:
1、null值的介紹
NULL 是數據庫中特有的數據類型,當一條記錄的某個列爲 NULL ,則表示這個列的值是未知的、是不肯定的。既然是未知的,就有無數種的可能性。所以, NULL 並非一個肯定的值。 這是 NULL 的由來、也是 NULL的基礎,全部和 NULL 相關的操做的結果均可以從 NULL 的概念推導出來。
2、oracle中的null值介紹
在不知道具體有什麼數據的時候,即未知,能夠用NULL, 稱它爲空,ORACLE中,含有空值的表列長度爲零。容許任何一種數據類型的字段爲空,除了如下兩種狀況:
a、主鍵字段(primary key);
b、定義時已經加了NOT NULL限制條件的字段
3、Oracle中null值說明:
a、等價於沒有任何值、是未知數。
b、NULL與0、空字符串、空格都不一樣。
c、對空值作加、減、乘、除等運算操做,結果 仍爲空。
d、NULL的處理使用NVL函數。
e、比較時使用關鍵字用「is null」和「is not null」。
f、空值不能被索引,因此查詢時有些符合條件的數據可能查不出來, count(expr)中,用nvl(列名,0)處理後再查。
g、排序時比其餘數據都大(索引默認是降序排列,小→大), 因此NULL值老是排在最後。
IS NULL 和IS NOT NULL 是不可分割的總體,改成IS 或IS NOT都是錯誤的,從上面咱們看到了NULL 和空字符串的區別。
任何和NULL 的比較操做,如<>、=、<=等都返回UNKNOWN(這裏的unknown就是null,它單獨使用和布爾值false相似).判斷和比較規則總結以下:
4、null作一些算術運算
好比+,-,*,/等,結果 仍是null,可是對於鏈接操做符||,null忽略,concat函數也忽略null
5、null相關函數規則
Oracle有nvl、nvl二、nullif、coalesce等函數專門處理null
nvl(expr1,expr2):若是expr1是null,那麼用expr2做爲返回值,不是null則返回expr1.expr1與expr2通常是類型相同的,若是類型不一樣則會採用自動轉換,轉換失敗則報錯。
nvl2(expr1,expr2,expr3):expr1若是是null,則返回expr3,不然返回expr2,expr2和expr3類型不一樣,expr3類型轉換爲expr2類型
nullif(expr1,expr2):判斷expr1和expr2是否相等,若相等則返回null,不然返回expr1.要求expr1與expr2類型必須相同
coalesce(expr1,expr2,…,exprn):從左到右返回第一個爲非null的值,若全部的列表元素都爲null,則返回null。要求全部都必須爲同一類型。
6、null與索引
Oracle中的B*Tree索引,並不存儲全爲null的列,雖然在表中創建了符合UNIQUE 索引,可是全爲null的行仍是能夠插入的,而不是全爲null的重複行則不能夠插入。由於在UNIQUE約束中,(null,null)和(null,null)是不一樣的,固然在其餘一些狀況,好比說分組、集合操做中都認爲全是null是相等的
7、null的排序
order by默認升序(asc),這時候null是排在最後的,若是指定降序那麼null是排在最前面的,認爲null最大。
可是能夠用nulls first和nulls last進行調整。order by comm asc nulls first/last
8、null與性能的關係
Not null約束,定義約束是要付出消耗性能的代價的,由下面的測試能夠看出雖然約束檢查的很快,可是有時候仍是很消耗資源的,至少在這個例子上是這樣的,不須要not null約束,除非必要,不要亂定義約束。
9、動態語句中的綁定變量與null
在PL/SQL中動態SQL和動態PL/SQL常用綁定變量,這個綁定變量有個要求,就是不能直接傳入字面量null值,由於PL/SQL中動態語句要求傳入的綁定變量必須是SQL類型,而字面量null是無類型的,null字面量傳入是不能夠的。
固然能夠採用多種方法,若是必定要傳入null,則能夠將null改成空字符串、TO_NUMBER,TO_CHAR,TO_DATE等函數進行轉換,或定義一個未初始化的變量、直接傳入變量等。即不能定義一參數賦值null,必須先給予其餘的賦值結果。
針對sqlserver的null的基本操做:
1、使用 =null / <>null 默認狀況下的確不能使用 =null / <> null 來判斷 null 值如此。實際上 SQL Server 能夠 使用 SET ANSI_NULLS { ON | OFF } 設定來控制 =null / <>null 的行爲。
當 SET ANSI_NULLS 爲 ON 時,即便 column_name 中包含空值,使用 WHERE column_name = NULL的 SELECT 語句仍返回零行。
即便 column_name 中包含非空值,使用 WHERE column_name <> NULL 的 SELECT 語句仍會返回零行
可是當 SET ANSI_NULLS 爲 OFF 時,等於 (=) 和不等於 (<>) 比較運算符不遵照 ISO 標準。
使用 WHERE column_name = NULL 的 SELECT 語句返回 column_name 中包含空值的行。
使用 WHERE column_name <> NULL 的 SELECT 語句返回列中包含非空值的行。
此外,使用 WHERE column_name <> XYZ_value 的 SELECT 語句返回全部不爲 XYZ_value 也不爲 NULL的行。
2、 改變 null 值的鏈接行爲 SQL Server 提供 SET CONCAT_NULL_YIELDS_NULL { ON | OFF } 來控制null 與其它字符串鏈接的行爲。
當 SET CONCAT_NULL_YIELDS_NULL 爲 ON 時,串聯空值與字符串將產生 NULL 結果。例如, SELECT 'abc' + NULL 將生成 NULL 。
當 SET CONCAT_NULL_YIELDS_NULL 爲 OFF 時,串聯空值與字符串將產生字符串自己(空值做爲空字符串處理)。例如, SELECT 'abc' + NULL 將生成 abc 。
若是未指定 SET CONCAT_NULL_YIELDS ,則應用 CONCAT_NULL_YIELDS_NULL 數據庫選項的設置。
注:在 SQL Server 的將來版本中, CONCAT_NULL_YIELDS_NULL 將始終爲 ON ,並且將該選項顯式設置爲 OFF 的任何應用程序都將產生一個錯誤。
3、變量的默認值與 null 值
命名一個變量後,若是沒有給它賦初始值,它的值就是 null 。有時候須要注意初始 null 值和經過 select 語句給變量後期賦 null 的區別。由於此 ‘null’ 非彼 ‘null’ 。
1. 子查詢中的 null
子查詢中出現的 null 值常常會被咱們忽視。
2. Case 語句中的 null
Case 中的 when 語句注意不要寫成 when null, 不然得不到想要的結果。
4、 與 null 相關的函數
ISNULL ISNULL 檢測表達式是否爲 NULL ,若是是的話替換 NULL 值爲另一個值 COALESCE COALESCE函數返回指定表達式列表的第一個非 NULL 值 NULLIF 當指定的兩個表達式有相同值的時候 NULLIF 返回 NULL值,不然返回第一個表達式的值
針對Sqlserver與Oracle中null值的不一樣:
1、在SQL Server中與oracle正相反,NULL值會被認爲是一個無窮小的值,因此若是按照升序排列的話,則會被排在最前面
2、SQL Server和Oracle中對插入數據值包含空的處理有所差別,在SQL Server中,咱們能夠把表字段設計爲非空,但咱們仍然能夠經過下面語句執行插入操做:
INSERT INTO Table (TestCol) VALUES(‘’)
其中的TestCol字段,在設計的時候,已經被設計爲NOT NULL在sql server中,null和空格是不一樣的,也就是說,上面的語句插入的是一個空,但並非NULL,只有當咱們的插入語句中沒有該字段的時候,纔會被認爲違反非空的條件約束,若是把NULL翻譯成「空」的話,可能就會很容易搞混了。此外,若是咱們的字段是INT類型的話,若是咱們插入空的話,會獲得一個0,也就是說,Sql server會自動幫咱們處理對空格的轉化。
可是在Oracle中,這個便利便不存在了,必須嚴格按照規則來進行插入,也就是說,咱們再想視圖經過插入空來知足NOT NULL的設計約束,已經不能成功了,必須插入實實在在的內容才能符合NOT NULL的約束。
注:這裏沒有將舉例copy過來,例子都是比較顯而易見的。瞭解了null的基本操做以後,就好尷尬啊。若是字段值中存儲了null值在oracle中咱們有想經過in的方式來查詢出來這個null值就基本不可能了。只有將字段進行函數處理nvl(column_name,’_NA_’) in(xxxx, ’_NA_’)來實現了,或者column_name in (xxxx) or column_name is null。
不過針對這些存儲了null的字段而且在查詢過程當中使用了in,多字段的查詢針對性的創建索引也是沒有什麼用的。null值也會給索引增長負擔,報表中的多條件查詢也不會同時都走上索引。只能乖乖的使用nvl來進行轉化後的篩選條件下的全掃描。
sql server 中order by 中關於null值處理
sqlserver 認爲 null 最小。
升序排列:null 值默認排在最前。
要想排後面,則:order by case when col is null then 1 else 0 end ,col
降序排列:null 值默認排在最後。
要想排在前面,則:order by case when col is null then 0 else 1 end , col desc一、on 、where、having中把unknown看成FALSE處理,使用篩選器爲unknown的行會排除在外,
而check約束中的unknown的值被當作true,假設一個check約束中要求salary大於0,插入salary爲null
的行能夠被接受 NUll > 0 的結果爲unknown
二、unique約束、排序、分組認爲兩個NULL是相等的
若是表的一列被定義爲unique約束,將不能插入兩個爲NULL值得行
group by 把全部null分爲一組
order by 把全部的null值排列在一塊兒select distinct top col from t order by col
先執行distinct –》order by –》top -
asp.net MVC漏油配置總結
URL構造
命名參數規範+匿名對象
routes.MapRoute(name:
"Default"
,url:
"{controller}/{action}/{id}"
, defaults:
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional } );
構造路由而後添加
Route myRoute =
new
Route(
"{controller}/{action}"
,
new
MvcRouteHandler());
routes.Add(
"MyRoute"
, myRoute);
直接方法重載+匿名對象
1routes.MapRoute(
"ShopSchema"
,
"Shop/{action}"
,
new
{ controller =
"Home"
});
我的以爲第一種比較易懂,第二種方便調試,第三種寫起來比較效率吧。各取所需吧。本文行文偏向於第三種。
路由規則
1.默認路由(MVC自帶)
routes.MapRoute(
"Default"
,
// 路由名稱
"{controller}/{action}/{id}"
,
// 帶有參數的 URL
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional }
// 參數默認值 (UrlParameter.Optional-可選的意思) );
2.靜態URL段
routes.MapRoute(
"ShopSchema2"
,
"Shop/OldAction"
,
new
{ controller =
"Home"
, action =
"Index"
});
routes.MapRoute(
"ShopSchema"
,
"Shop/{action}"
,
new
{ controller =
"Home"
});
routes.MapRoute(
"ShopSchema2"
,
"Shop/OldAction.js"
,
new
{ controller =
"Home"
, action =
"Index"
});
沒有佔位符路由就是現成的寫死的。
好比這樣寫而後去訪問http://localhost:XXX/Shop/OldAction.js,response也是徹底沒問題的。 controller , action , area這三個保留字就別設靜態變量裏面了。
3.自定義常規變量URL段(好吧這翻譯暴露智商了)
routes.MapRoute(
"MyRoute2"
,
"{controller}/{action}/{id}"
,
new
{ controller =
"Home"
, action =
"Index"
, id =
"DefaultId"
});
這種狀況若是訪問 /Home/Index 的話,由於第三段(id)沒有值,根據路由規則這個參數會被設爲DefaultId
這個用viewbag給title賦值就能很明顯看出
ViewBag.Title = RouteData.Values[
"id"
];
圖不貼了,結果是標題顯示爲DefaultId。 注意要在控制器裏面賦值,在視圖賦值無法編譯的。
4.再述默認路由
而後再回到默認路由。 UrlParameter.Optional這個叫可選URL段.路由裏沒有這個參數的話id爲null。 照原文大體說法,這個可選URL段能用來實現一個關注點的分離。剛纔在路由裏直接設定參數默認值其實不是很好。照個人理解,實際參數是用戶發來的,咱們作的只是定義形式參數名。可是,若是硬要給參數賦默認值的話,建議用語法糖寫到action參數裏面。好比:
1public
ActionResult Index(
string
id =
"abcd"
){ViewBag.Title = RouteData.Values[
"id"
];
return
View();}
5.可變長度路由。
1routes.MapRoute(
"MyRoute"
,
"{controller}/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional });
在這裏id和最後一段都是可變的,因此 /Home/Index/dabdafdaf 等效於 /Home/Index//abcdefdjldfiaeahfoeiho 等效於 /Home/Index/All/Delete/Perm/.....
6.跨命名空間路由
這個提醒一下記得引用命名空間,開啓IIS網站否則就是404。這個很是非主流,不建議瞎搞。
1routes.MapRoute(
"MyRoute"
,
"{controller}/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
[] {
"URLsAndRoutes.AdditionalControllers"
,
"UrlsAndRoutes.Controllers"
});
可是這樣寫的話數組排名不分前後的,若是有多個匹配的路由會報錯。 而後做者提出了一種改進寫法。
routes.MapRoute(
"AddContollerRoute"
,
"Home/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
[] {
"URLsAndRoutes.AdditionalControllers"
});
routes.MapRoute(
"MyRoute"
,
"{controller}/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
[] {
"URLsAndRoutes.Controllers"
});
這樣第一個URL段不是Home的都交給第二個處理 最後還能夠設定這個路由找不到的話就不給後面的路由留後路啦,也就再也不往下找啦。
1234Route myRoute = routes.MapRoute(
"AddContollerRoute"
,
"Home/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
[] {
"URLsAndRoutes.AdditionalControllers"
}); myRoute.DataTokens[
"UseNamespaceFallback"
] =
false
;
7.正則表達式匹配路由
1234routes.MapRoute(
"MyRoute"
,
"{controller}/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
{ controller =
"^H.*"
},
new
[] {
"URLsAndRoutes.Controllers"
});
約束多個URL
1234routes.MapRoute(
"MyRoute"
,
"{controller}/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
{ controller =
"^H.*"
, action =
"^Index$|^About$"
},
new
[] {
"URLsAndRoutes.Controllers"
});
8.指定請求方法
1234567routes.MapRoute(
"MyRoute"
,
"{controller}/{action}/{id}/{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
, id = UrlParameter.Optional },
new
{ controller =
"^H.*"
, action =
"Index|About"
, httpMethod =
new
HttpMethodConstraint(
"GET"
) },
new
[] {
"URLsAndRoutes.Controllers"
});
9. WebForm支持
1234567routes.MapPageRoute(
""
,
""
,
"~/Default.aspx"
);
routes.MapPageRoute(
"list"
,
"Items/{action}"
,
"~/Items/list.aspx"
,
false
,
new
RouteValueDictionary { {
"action"
,
"all"
} });
routes.MapPageRoute(
"show"
,
"Show/{action}"
,
"~/show.aspx"
,
false
,
new
RouteValueDictionary { {
"action"
,
"all"
} });
routes.MapPageRoute(
"edit"
,
"Edit/{id}"
,
"~/edit.aspx"
,
false
,
new
RouteValueDictionary { {
"id"
,
"1"
} },
new
RouteValueDictionary { {
"id"
,
@"\d+"
} });
具體的能夠看
使用Asp.Net4新特性路由建立WebForm應用
或者官方msdn
10.MVC5的RouteAttribute
首先要在路由註冊方法那裏
12//啓用路由特性映射
routes.MapMvcAttributeRoutes();
這樣
1[Route(
"Login"
)]
route特性纔有效.該特性有好幾個重載.還有路由約束啊,順序啊,路由名之類的.
其餘的還有路由前綴,路由默認值
1[RoutePrefix(
"reviews"
)]<br>[Route(
"{action=index}"
)]<br>
public
class
ReviewsController : Controller<br>{<br>}
路由構造
1234567// eg: /users/5
[Route(
"users/{id:int}"
]
public
ActionResult GetUserById(
int
id) { ... }
// eg: users/ken
[Route(
"users/{name}"
]
public
ActionResult GetUserByName(
string
name) { ... }
參數限制
12345// eg: /users/5
// but not /users/10000000000 because it is larger than int.MaxValue,
// and not /users/0 because of the min(1) constraint.
[Route(
"users/{id:int:min(1)}"
)]
public
ActionResult GetUserById(
int
id) { ... }
Constraint Description Example alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha} bool Matches a Boolean value. {x:bool} datetime Matches a DateTime value. {x:datetime} decimal Matches a decimal value. {x:decimal} double Matches a 64-bit floating-point value. {x:double} float Matches a 32-bit floating-point value. {x:float} guid Matches a GUID value. {x:guid} int Matches a 32-bit integer value. {x:int} length Matches a string with the specified length or within a specified range of lengths. {x:length(6)} {x:length(1,20)} long Matches a 64-bit integer value. {x:long} max Matches an integer with a maximum value. {x:max(10)} maxlength Matches a string with a maximum length. {x:maxlength(10)} min Matches an integer with a minimum value. {x:min(10)} minlength Matches a string with a minimum length. {x:minlength(10)} range Matches an integer within a range of values. {x:range(10,50)} regex Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)} 具體的能夠參考
Attribute Routing in ASP.NET MVC 5
對我來講,這樣的好處是分散了路由規則的定義.有人喜歡集中,我我的比較喜歡這種靈活的處理.由於這個action定義好後,我不須要跑到配置那裏定義對應的路由規則
11.最後仍是不爽的話本身寫個類實現 IRouteConstraint的匹配方法。
1234567891011121314151617181920212223using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.Routing;
/// <summary>
/// If the standard constraints are not sufficient for your needs, you can define your own custom constraints by implementing the IRouteConstraint interface.
/// </summary>
public
class
UserAgentConstraint : IRouteConstraint
{
private
string
requiredUserAgent;
public
UserAgentConstraint(
string
agentParam)
{
requiredUserAgent = agentParam;
}
public
bool
Match(HttpContextBase httpContext, Route route,
string
parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
return
httpContext.Request.UserAgent !=
null
&&
httpContext.Request.UserAgent.Contains(requiredUserAgent);
}
}
1234567routes.MapRoute(
"ChromeRoute"
,
"{*catchall}"
,
new
{ controller =
"Home"
, action =
"Index"
},
new
{ customConstraint =
new
UserAgentConstraint(
"Chrome"
) },
new
[] {
"UrlsAndRoutes.AdditionalControllers"
});
好比這個就用來匹配是不是用谷歌瀏覽器訪問網頁的。
12.訪問本地文檔
123routes.RouteExistingFiles =
true
;
routes.MapRoute(
"DiskFile"
,
"Content/StaticContent.html"
,
new
{ controller =
"Customer"
, action =
"List"
, });
瀏覽網站,以開啓 IIS Express,而後點顯示全部應用程序-點擊網站名稱-配置(applicationhost.config)-搜索UrlRoutingModule節點
1<add name=
"UrlRoutingModule-4.0"
type=
"System.Web.Routing.UrlRoutingModule"
preCondition=
"managedHandler,runtimeVersionv4.0"
/>
把這個節點裏的preCondition刪除,變成
1<add name=
"UrlRoutingModule-4.0"
type=
"System.Web.Routing.UrlRoutingModule"
preCondition=
""
/>
13.直接訪問本地資源,繞過了路由系統
1routes.IgnoreRoute(
"Content/{filename}.html"
);
文件名還能夠用 {filename}佔位符。
IgnoreRoute方法是RouteCollection裏面StopRoutingHandler類的一個實例。路由系統經過硬-編碼識別這個Handler。若是這個規則匹配的話,後面的規則都無效了。 這也就是默認的路由裏面routes.IgnoreRoute("{resource}.axd/{*pathInfo}");寫最前面的緣由。
路由測試(在測試項目的基礎上,要裝moq)
1PM> Install-Package Moq
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899using
System;
using
Microsoft.VisualStudio.TestTools.UnitTesting;
using
System.Web;
using
Moq;
using
System.Web.Routing;
using
System.Reflection;
[TestClass]
public
class
RoutesTest
{
private
HttpContextBase CreateHttpContext(
string
targetUrl =
null
,
string
HttpMethod =
"GET"
)
{
// create the mock request
Mock<HttpRequestBase> mockRequest =
new
Mock<HttpRequestBase>();
mockRequest.Setup(m => m.AppRelativeCurrentExecutionFilePath)
.Returns(targetUrl);
mockRequest.Setup(m => m.HttpMethod).Returns(HttpMethod);
// create the mock response
Mock<HttpResponseBase> mockResponse =
new
Mock<HttpResponseBase>();
mockResponse.Setup(m => m.ApplyAppPathModifier(
It.IsAny<
string
>())).Returns<
string
>(s => s);
// create the mock context, using the request and response
Mock<HttpContextBase> mockContext =
new
Mock<HttpContextBase>();
mockContext.Setup(m => m.Request).Returns(mockRequest.Object);
mockContext.Setup(m => m.Response).Returns(mockResponse.Object);
// return the mocked context
return
mockContext.Object;
}
private
void
TestRouteMatch(
string
url,
string
controller,
string
action,
object
routeProperties =
null
,
string
httpMethod =
"GET"
)
{
// Arrange
RouteCollection routes =
new
RouteCollection();
RouteConfig.RegisterRoutes(routes);
// Act - process the route
RouteData result = routes.GetRouteData(CreateHttpContext(url, httpMethod));
// Assert
Assert.IsNotNull(result);
Assert.IsTrue(TestIncomingRouteResult(result, controller, action, routeProperties));
}
private
bool
TestIncomingRouteResult(RouteData routeResult,
string
controller,
string
action,
object
propertySet =
null
)
{
Func<
object
,
object
,
bool
> valCompare = (v1, v2) =>
{
return
StringComparer.InvariantCultureIgnoreCase
.Compare(v1, v2) == 0;
};
bool
result = valCompare(routeResult.Values[
"controller"
], controller)
&& valCompare(routeResult.Values[
"action"
], action);
if
(propertySet !=
null
)
{
PropertyInfo[] propInfo = propertySet.GetType().GetProperties();
foreach
(PropertyInfo pi
in
propInfo)
{
if
(!(routeResult.Values.ContainsKey(pi.Name)
&& valCompare(routeResult.Values[pi.Name],
pi.GetValue(propertySet,
null
))))
{
result =
false
;
break
;
}
}
}
return
result;
}
private
void
TestRouteFail(
string
url)
{
// Arrange
RouteCollection routes =
new
RouteCollection();
RouteConfig.RegisterRoutes(routes);
// Act - process the route
RouteData result = routes.GetRouteData(CreateHttpContext(url));
// Assert
Assert.IsTrue(result ==
null
|| result.Route ==
null
);
}
[TestMethod]
public
void
TestIncomingRoutes()
{
// check for the URL that we hope to receive
TestRouteMatch(
"~/Admin/Index"
,
"Admin"
,
"Index"
);
// check that the values are being obtained from the segments
TestRouteMatch(
"~/One/Two"
,
"One"
,
"Two"
);
// ensure that too many or too few segments fails to match
TestRouteFail(
"~/Admin/Index/Segment"
);
//失敗
TestRouteFail(
"~/Admin"
);
//失敗
TestRouteMatch(
"~/"
,
"Home"
,
"Index"
);
TestRouteMatch(
"~/Customer"
,
"Customer"
,
"Index"
);
TestRouteMatch(
"~/Customer/List"
,
"Customer"
,
"List"
);
TestRouteFail(
"~/Customer/List/All"
);
//失敗
TestRouteMatch(
"~/Customer/List/All"
,
"Customer"
,
"List"
,
new
{ id =
"All"
});
TestRouteMatch(
"~/Customer/List/All/Delete"
,
"Customer"
,
"List"
,
new
{ id =
"All"
, catchall =
"Delete"
});
TestRouteMatch(
"~/Customer/List/All/Delete/Perm"
,
"Customer"
,
"List"
,
new
{ id =
"All"
, catchall =
"Delete/Perm"
});
}
}
最後仍是再推薦一下Adam Freeman寫的apress.pro.asp.net.mvc.4這本書。
-
-