2016年4月28日 星期四

Functional requirement & Non-Functional requirement

要寫好一份'軟體需求規格文件' (SRS) 不容易。 '使用者需求'是它的Input,'軟體設計文件' 是它的Output。
[User Requirements] --> [SRS] --> [SW Design (High Level Design + Detailed Level Design)]

什麼樣的SRS才是好的? 下列四點是個不錯的判斷依據:
1. 提供回饋給user。最好的回饋方式就是讓User看到! 比如UI、Output files, Tools
2. 用軟體的觀點來看問題,把問題分解成幾個SW components。What (SW components, Tools) should be enhanced/changed/updated/removed/created...
3. SRS是SW design 的Input。SRS 只需定義'規格' (What),它是一份合約。實作方式則定義在SW Design (How)。
4. SRS 必須用'外顯'、'可量測' 的角度來撰寫,好讓Tester判斷正確性。

看了許多的 SRS, 節錄出下列幾個重要方向:
Functional requirement: 定義系統要做的事 (What the system does)
Business Rules
External Interfaces (ex: UI, output files...)
Audit Tracking (ex: Logging)
Transaction corrections, adjustments and cancellations
Administrative functions
Authentication
Authorization levels
Certification Requirements
Reporting Requirements
Historical Data
Legal or Regulatory Requirements

Non-functional requirements: 定義系統的能力 (What the system's capability - the power to do it)
Reliability - 可以依據既定規格而持續運行的能力
Serviceability
Availability
Performance – for example Response Time, Throughput, Utilization, Static Volumetric
Scalability -  To handle a growing amount of work (系統處理工作量的成長)
Capacity - 系統可以產出(或提供服務)的最大量
Recoverability
Maintainability: 維持原有的形式,萬一發生錯誤也可以回復到原有的形式
Security
Regulatory
Manageability
Environmental
Data Integrity
Usability
Interoperability


Non-functional 影響層面有:
  • Reliability: Improvement in number of hits [hits/ syst yr] (Hit 次數)
  • Serviceability: Improvement on Maintenance and in MTTR (diagnostics, access, repair and/or recovery) repairing of system [hrs] (維修, 回復 時數)
  • Availability: Improvement in hours of downtime both USD (Unscheduled) as well as SD (Scheduled) [hrs/syst yr] (Downtime 時數)
  • Manufacturability: Improvement in CT (Cycle Time) or A-time in production [hrs/ syst]
  • Safety: Improvement of probability of incidents occurring
  • Usability: Functional improvement of machine for operator
  • Machine Performance: Non-functional improvement of customer requirements



2016年4月18日 星期一

The Benefits of Isolating Applications (隔離應用程式的優點)

本文先說明應用程式'隔離'(isolation)的概念, 接著再說明.NET 實作'隔離'的方式。

[應用程式'隔離'的概念]
在過去,Process boundaries 被用來達到 app 隔離的效果。App 必須被隔離是因為'記憶體位址' (memory address) 是 process 相關 (process-relative)。也就是說,這個 process 使用的 memory address 不能被另一個 process 使用。
因為 memory address 不能共用,因此,兩個 processes 之間也不能直接互相呼叫,也就是不能直接存取程式碼 (必須使用 proxies, 例如 Inter-process communication)。

而現在,managed code 在被執行之前,必須通過'權限驗證'(permission verification)才能被執行 (除非  administrator 已經授權 skip此驗證 )。權限驗證主要是驗證下列兩件事情:
  1. 程式碼是否可能嘗試存取無效的memory address
  2. 程式碼是否可能無法正常運行
如果上述的驗證通過了,此 managed code 稱為 型別安全 (type-safe) [** 注意: 這裡指的 type safe,指的是memory type safety (參閱 這裡),而不是廣義的type safe定義 (例如 integer 與 string 的型別)]。這種'權限驗證'提供了 CLR 具有與過去Process boundaries 相同的app 隔離效果,而且效率更好!

上述的說明,概念上解釋了隔離的重要性。接下來,說明.NET 如何實作隔離

[.NET實作'隔離'的方式]
在.NET中,使用 AppDomain 可以讓 CLR 提供 app 的隔離效果。CLR 允許單一個process裡面執行多個AppDomains,也就是說,單一個processe可以執行多個 apps,如此便增加了 server 端的 scalability。

AppDomain 具有下列優點:
  1). 在某一應用程式中的錯誤不會影響其他應用程式。由於 type-safe 程式碼不會導致記憶體錯誤,所以使用 AppDomain 可確保在某一Domain中執行的程式碼不會影響處理序(process)裡面的其他應用程式。

  2). 可以停止個別應用程式而不需停止整個處理序(process)。使用 AppDomain 可以卸載在單一個 app 中執行的程式碼。[** 注意: 這裡指的是 "卸載一個完整的AppDomain",而不是卸載一個DLL) (卸載AppDomain的方法, 參閱 這裡 ]

  3). 可以防止不同 AppDomain 中對於物件的呼叫。在不同定義域之間傳遞的物件必須用'複製方式傳遞'或由 'Proxy' 存取。
    3-1). 如果物件是複製的,那麼對該物件的呼叫就是區域呼叫。也就是說,呼叫端(caller)和被參考的物件是在同一個AppDomain。
    3-2). 如果物件是透過 Proxy 存取,那麼對物件的呼叫便是遠端呼叫(call remotely)。在這種情況下,呼叫端(caller)和被參考的物件(referenced object)是在不同的應用程式定義域中。跨定義域呼叫(Cross-domain call)是使用與兩個處理序(process)或兩部電腦之間呼叫相同的'遠端呼叫基礎結構'(remote call infrastructure)。因此,所參考之物件的中繼資料 (Metadata) 必須在這兩個AppDomain 中都能使用,才能讓該方法呼叫被 JIT 適當地編譯。如果caller 無權存取所呼叫之物件的中繼資料(Metadata),則編譯可能會失敗,並擲回型別 System.IO.FileNotFound 的例外狀況。

4). 程式碼的行為範圍(behavior scope)是由該應用程式決定。換言之,AppDomain會提供 app 版本原則、它所存取的遠端組件的位置,以及有關哪裡可以載入組件的資訊。



相關連結:
The Benefits of Isolating Applications: MSDN

2016年4月17日 星期日

Regex Tester (Regular Expressions 線上debug pattern)

發現一個不錯的網站, 可以直接在線上 debug 與 test Regular Expressions的 pattern,而且它還有解釋 pattern 的每個意義:

  regular expressions 101



先關連結:
Regular Expression的學習筆記

什麼是 AppDomain (Application domains, 應用程式定義域)

OS 和 CLR 提供了某種 '隔離' (isolatation) 的概念來確保 process 和 process 之間不會相互影響。Application domains 就是這種 '隔離' (isolatation) 的概念。有了AppDomain,CLR便可以提供組件的安全性 (security)、可靠性 (reliability) 、 版本控制(versioning) 以及 卸載組件(unloading assemblies) 。

AppDomain 是由runtime host 所建立。Runtime host 啟動 CLR之後, 然後再 開始執行 app 。也就是說,Runtime host 會為這個 app 建立一個AppDomain

有關什麼是 AppDomain的詳細說明, 請參閱 這裡


相關連結:
Application Domains : MSDN

2016年4月13日 星期三

NUnit gets wrong configuration file (NUnit test 讀不到 app.config)

[Problem description]
要讀取應用程式的app.config,若使用下列的 API,在 NUnit 環境下可能會讀取到錯誤的 config:

if (File.Exists(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile))
{
    // do something
}

雖然使用此 API,在執行應用程式時可以正確回傳應用程式的config 檔案 (ex: <MyAppName>.exe.config),可是在 NUnit 環境下,使用此API 卻會回傳另外的 config 檔案!
例如:
  • 使用 VS2010 + Resharper (v6.1) 會回傳 ~\UnitTest\bin\Debug 目錄下的 UnitTest.dll.config 
  • 使用 VS2015 + Resharper (v10.0) 會回傳 %temp% 目錄下某個隨機子目錄的 config 檔案
這種不確定性會造成 unit test 不夠 robust, 而且上述兩種環境都無法正確讀取到<MyAppName>.exe.config。

[Root cause]
AppDomain.CurrentDomain 可以用來取得目前應用程式的 domain 。
  1. 對於應用程式來說, AppDomain.CurrentDomain 指的是 exe 所在的目錄,一般是位於~\bin\Debug 
  2. 對於 NUnit  來說,它沒有 exe 檔案,所以指的是 UnitTest.dll 所在的目錄: ~\UnitTest\bin\Debug
  3. 然而, 使用 VS2015 + Resharper (v10.0) 卻指向 %temp% ,這個應該是設定上的問題。
既然在使用NUnit 上透過 AppDomain.CurrentDomain 只能取得 NUnit 自己的 domain ,無法取得我們想要的應用程式 domain ,因此無法透過此API 取得 <MyAppName>.exe.config

[Solution]
有下列幾種 work around:
  1). 改用應用程式的 Settings.setting 取代 <MyAppName>.exe.config
  2). 移除 app.config 與 MyAppName 的直接相依性, 改用 dependency injection 來建立 app.config 與 MyAppName的關係,如此便很方便 Mock 這個 app.config 在NUnit的程式碼當中




相關連結:
app.config 與 Settings.setting 的差異: MSDN forums, StackOverflow, User settings - MSDN
什麼是 AppDomain
AppDomain.CurrentDomain 屬性
Application 與 AppDomain.CurrentDomain 的差異

2016年4月11日 星期一

IsHandleCreated, InvokeRequired 與 Invoke 的關係

要存取 Windows Forms Controls 就必須考慮 Thread-Safe的問題 (參閱: 這裡)。如果 InvokeRequired 是 True,就必須使用 Invoke method 來保證 Thread-Safe。

然而,要使用 Invoke method,必須確定到這個 Form 或是 Control 已經擁有 '視窗控制代碼' (window handle)。
在下列的情況下,Handle 才會存在:
  • Form/Control 已經實體化( instantiated )且初始化 (initialized, first shown)
  • Form 還沒被 closed (i.e., handle 還沒被 disposed)
** 要注意的是,使用InvokeRequired 判斷是否為 cross-thread 並不正確! 因為 InvokeRequired 在下列2種情況會回傳 False:
  1. 相同 thread (表示不需要 Invoke)
  2. Form/Control 沒有 handle (沒有 handle 就不可能 Invoke)
也就是說,InvokeRequired  只能判斷要不要 Invoke!

若要使用InvokeRequired 判斷 cross-thread,必須排除 handle 不存在所造成的影響。
比較好的做法是: 使用 InvokeRequired 之前,先判斷 IsHandleCreated 為True

[例如]:
private delegate void SetTextDelegate(string text, Color color, Font font);

private void SetText(string text, Color color, Font font)
{
            if (IsHandleCreated)
            {
                 // 已經排除沒有 handle 的情況
                 // 所以可用InvokeRequired 判斷是否為 cross-thread
                if (InvokeRequired)                
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    Invoke(d, new object[] { text, color, font });
                }
                else // use the RichTextBox control directly
                {
                    txtLogging.AppendText(text);
                    txtLogging.SelectionColor = color;
                    txtLogging.SelectionFont = font;
                 }
            }
}

相關連結:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created


Cross-thread operation not valid on Windows Form Control

Windows Forms Controls 的存取不是安全執行緒(Thread-Safe)。 如果有兩個以上的執行緒在管理 Control 狀態,則可能會造成 Control 進入不一致的狀態, 也可能發生其他與執行緒相關的錯誤 - 例如競爭情況 (race conditions) 和死結 (deadlock)。 因此,必須確定 Control 存取是以Thread-Safe的方式執行。

最常發生的錯誤情況是,建立 Control 的執行緒 (i.e., Windows Form, creating thread) 與 存取Control 的執行緒 (i.e., 某個calling thread) 並不是同一個Thread,而造成 Cross-thread operation not valid 錯誤。此時,使用 Invoke method 可以解決這個問題。
(** 所謂的 Invoke method,其實只是把 delegate 與 input arguments 傳進去, 讓  Windows Form 去執行此 delegate。 參閱: Control.Invoke方法)

對 Windows Forms Controls 進行Thread-Safe 存取的作法如下:
  1. 查詢 Controls 的 InvokeRequired 屬性。( InvokeRequired 用來 compares the thread ID )
  2. 如果 InvokeRequired 回傳 true ,不同 thread 要使用 Invoke;
  3. 不然的話,相同thread 則直接存取Control 。 
[例如]:
private delegate void SetTextDelegate(string text, Color color, Font font);

private void SetText(string text, Color color, Font font)
{
                 if (InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    Invoke(d, new object[] { text, color, font });
                }
                else // use the RichTextBox control directly
                {
                    txtLogging.AppendText(text);
                    txtLogging.SelectionColor = color;
                    txtLogging.SelectionFont = font;
                 }
}


利用VS 2015執行應用程式時,如果VS 2015引發 InvalidOperationException,並且會有訊息:"Control 'control name' accessed from a thread other than the thread it was created on",這就表示此 Control 正在被 none Thread-Safe 的行為存取, 此時必須用Invoke method 來解決。


See the following Q&A (from https://bbs.csdn.net/topics/390581976)
1. 為什麼更新某個 Textbox 的狀態, 不能夠直接寫 Textbox="123" 就好了?
  • 為了Thread-Safe 。微軟建議你在創建 Control 的那個 Thread 裡面去更新 Control 。如果只是 Textbox="123", CLR怎麼知道你是想做 Thread-Safe 的操作? 還是 none Thread-Safe 的操作呢?

 

2. 為什麼一定要搞個 delegate 要我們去寫的這麼複雜?
  • delegate 與 multi-thread 更新 Control 沒有什麼必然關係, 它只是指定了你更新 Control 的時候執行的具體動作

2016年4月9日 星期六

System.BadImageFormatException: Could not load file or assembly '...' ( Platform target 不一致)

[Problem description]
當某個 Project A (ex: unit test project) 使用某個 Project B,此時 compile 沒有問題,但是執行 Project A 卻發生下列錯誤訊息:

System.BadImageFormatException : Could not load file or assembly 'Project B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was made to load a program with an incorrect format.

[Root cause]
Project A 與 Project B 並不同為是 32-bit (或是 64-bit) 平台 (target platform)

[Solution]
編輯這兩個Projects 的 Platform target 同為 x86 (或是 64),如下:

2016年4月7日 星期四

Use Costura.Fody to bundle all assemblies into one ( 比 ILMerge 更好用 )

不論是 ILMerge 或是 LibZ,使用上都有一些限制 (參閱: 這裡)
在此,介紹一下 Costura.Fody ,它比 ILMerge或Libz更強大好用:

  • 可將 reflection, native DLLs, 或是3rd party 有使用 reflection 的應用程式整合成一個執行檔。
  • 可把 WPF 應用程式與其他 .Net 的DLL檔案 merge 成為單一執行檔。
  • 比 ILMerge 有更加的壓縮比率。大概是 ILMerge 壓縮出來執行檔的70%。


1).下載 Costura.Fody - 使用VS2015 的 NuGet Package Manager
  • 由 Reference 的 Manage NuGet Packages...下載 Costura.Fody (做法同 LibZ 的下載方式)

2).使用 Costura.Fody 來bundle 所有的 assemblies:
  • 下載之後,在這個project 底下有一個FordyWeavers.xml,編輯此 FodyWeavers.xml,把debug symbols 排除,如下:
          <?xml version="1.0" encoding="utf-8"?>
          <Weavers>
                <Costura IncludeDebugSymbols='false' />
          </Weavers>
  • 然後,在 VS2015 上面 Build 這個 project,<MyApp.exe> 檔案 size 會變大,變成一個單一執行檔。
       *** Costura.Fody 也可以直接用在 WPF app 上面!  夠簡單吧!

3).下載安裝Costura.Fody 之後,若要在VS2015設定 break point 來 debug 了,可以把 .csproj檔案修改如下:
  • <Import Project="~\..\..\Fody.targets" Condition=" '$(Configuration)' == 'Release' " />

相關連結: