.net教學筆記整理 [組態檔案驅動設計架構例子]

前言:
公司經常會遇到一些不是給使用者用的程式,例如一些格式轉換程式,一些上傳到某些地方的程式,或者背景服務等等。這些服務具有一共通點:

  1. 參數設定很多,如SMTP Server, 資料庫IP。
  2. 要經常改變參數設定。
參數寫死在軟體程式裡面,第一個缺點當然是不好修改,而且不斷修改程式碼會引發不必要的錯誤。所以現在很多伺服器都是採用了以XML組態檔案驅動驅動的設計架構。意思就是說利用XML定義很多軟體參數,當系統運行的時候才去讀取相關設定,這樣軟體不需要經常重新修改,比較能確保穩定度。這種概念也就是所謂的執行引擎了(Execute Engine)

以下是小弟最近利上執行引擎的概念幫公司寫的小軟體,希望可以給各位大大分享一下,有錯誤的話請賜教。

目的:
把資料庫的某一資料表(table)轉成XML, 並自動上傳到指定FTP位置。這個小軟體要具有:
1. email功能: 上傳成功或失敗需要發email通知資訊部門某位同事。
2. 紀錄檔(log)功能: 運行時產生相關程序的記錄,包括exception。
3. 上傳到FTP伺服器前要先備份。
4. 產生的XML檔案名稱將來可能會有修改。

設計:
要表達一個系統的設計,至少要表明兩種關係
  • Class 之間的關係
  • 觸發事件的先後次序
Class 之間的關係
由以上需求可知,如果我們在物件導向分析的類別圖(Class diagram)中開始設計類別,可以以功能區分最初版本的5個類別,分別是:
Program.cs: 提供軟體核心作業流程。
xmlManager.cs: 提供轉換成XML的相關功能。
emailManager.cs: 提供傳送電子郵件的功能。
ftpManager.cs: 提供ftp上載的功能。
logManager.cs: 提供記錄檔相關功能。

它們這些物件與物件之間的關係,可以用UML中的Class diagram表達:
  • 虛線指的是這一個class可以存取的檔案。
  • 實心菱型在UML中解釋是Composition(組合), 意思相當於has a, 就是說被指向的Class(xmlManager跟logManager)是菱型端的Class(Program.cs)不可缺少的一部分, 當Program.cs被消滅的時候xmlManager跟logManager就一齊消失了,同理當Program.cs出現的時候一定要有xmlManager跟logManager這兩個Class.

    還是不明白? 再舉一個例子。例如一台汽車具有四個輪子,汽車本身就是Program.cs,輪子就是 xmlManager跟logManager。當輪子消失的時候,汽車也不能跑了。
  • 空心菱型在UML中解釋是Aggregation(聚合)。是一種大家不依賴大家也可以獨立運行,比較弱的關係。xmlManager跟emailManager還有ftpManager就是這種關係,整支程式就算沒有email也是可以跑得順順的。同理,emailManager自己可以控制發電郵的機制,完全不需要這隻轉換程式的幫助。拿回上面的例子來說,就像汽車跟人的關係,汽車沒有人開還是汽車,人沒車也是獨立的個體。
觸發事件的先後次序 
Class定好以後,可以利用sequence diagram來表達Class的流程圖。下面是整個程式流程的簡化版本:(圖太小, 按一下可看全圖)



  • main是整個程式的還輯控制核心元件。

Coding技術解說:

app.config檔案:
這是官方的參數設定檔案
  1. 如要儲存連接db的connectionString,如下:
  2.   <connectionStrings>
        <add name="SQRConnectionString" connectionString="Data Source=xxx.xxx.xxx.xxx;Initial Catalog=xxx;User ID=xxxx;Password=xxx" providerName="System.Data.SqlClient" />
      </connectionStrings>

  3. 如要儲存其他參數設定, 如下:
    這裡共包含12項參數,包括
    * XML檔案存放位置
    * XML檔案備份位置
    * XML產生的檔案名稱
    * XML上傳到FTP後的名稱
    * FTP伺服器IP
    * FTP伺服器登入名稱
    * FTP伺服器登入密碼
    * FTP伺服器登入後要去的路徑
    * SMTP伺服器IP
    * 郵件發出者
    * 電子郵件清單(用";"分隔)
    * log存放的位置
  4.   <appSettings>
        <add key="smtp" value="" />
        <add key="xmlLocal" value="/result/" />
        <add key="xmlBkp" value="/bkp/" />
        <add key="resultName" value="xxx.xml" />
        <add key="FTPtargetFileName" value="xxx.xml" />
        <add key="FTPhost" value="ftp://xxx.xxx.xxx.xxx" />
        <add key="FTPusername" value="xxx" />
        <add key="FTPpassword" value="xxx" />
        <add key="FTPremoteDir" value="/csv/" />
        <add key="smtp" value="xxx.xxx.xxx.xxx" />
        <add key="smtpFrom" value="terencemak84@gmail.com" />
        <add key="smtpTo" value="terencemak84@gmail.com;terencemak84@gmail.com" />
        <add key="logLocation" value="/log/" />
        <add key="ClientSettingsProvider.ServiceUri" value="" />
      </appSettings>
    
其他的資料VS會自動產生,所以開發者也不必操心了。

讀取app.config的參數:

  1. 讀取資料庫可用
  2. var dbsettings = ConfigurationManager.ConnectionStrings["SQLConnectionString"]; //read sql setting
    

  3. 讀取其他參數可用
  4. ConfigurationManager.AppSettings["參數key名稱"]
    
把資料庫資料轉成XML
  1. 先把資料庫轉成Dataset (請參考之前的教學資料庫篇)
  2. 假設Dataset為ds, 輸出成XML可以寫成即可。
  3. ds.WriteXml("路徑\檔案名稱")
    
檢查檔案是否已經存在
  • 可利用FileInfo類別中的 File.exists方法。
  •             bool result = false;
                string ResultFileLocation = xmlManager.getXmlLocalResultPath() + ConfigurationManager.AppSettings["resultName"];
                FileInfo resultFile = new FileInfo(ResultFileLocation);
                if (resultFile.Exists)
                {
                    result = true;
                }
                else
                {
                    result = false;
                }
                return result;
    
移動檔案
  • 可利用File.MoveTo()方法。如下面例子只要把移動路徑跟檔案名稱丟進方法就可以了。
  •         public static void bkpResultXml()
            {
                if (ResultXmlExist() == true)
                {
                    string bkpResultFileLocation = xmlManager.getXmlLocalResultPath();
                    FileInfo bkpResultFile = new FileInfo(bkpResultFileLocation + ConfigurationManager.AppSettings["resultName"]);
                    string bkpLocation = xmlManager.getXmlLocalBkpPath();
                    bkpResultFile.MoveTo(bkpLocation + DateTime.Now.ToString("yyyyMMddhhmmss")+"_"+ ConfigurationManager.AppSettings["resultName"]);
                }
            }
    
ftp上傳檔案
利用C#上傳檔案到ftp伺服器需要用WebRequest及WebResponse兩個類別, 使用步驟如下:

  1. 宣告一FtpWebResponse 及 FtpWebRequest物件.
  2. 在FtpWebRequest物件中指定ftp server的位置, 相關路徑及檔案名稱
  3. Method指定為上傳.
  4. 利用Credentials指定ftp登入名稱及密碼
  5. 設定些相關參數
  6. 把檔案轉換成byte資料流
  7. 上傳
  8. 接收回覆訊息
例子:
        public static void FTPUpload( Uri serverUri, string sourceFile, string ftpDirectory, string targetFileName, string username, string pwd, out bool success, out string responseMsg)
        {
            success = false;
            //建立物件
            FtpWebResponse ftpUploadResponse=null;
            FtpWebRequest UpdRequest = (FtpWebRequest)WebRequest.Create(serverUri+ftpDirectory + targetFileName);
            //方法設為上傳
            UpdRequest.Method = WebRequestMethods.Ftp.UploadFile;
            //設定登入使用者及密碼
            UpdRequest.Credentials = new NetworkCredential(username, pwd);
            //設定參數
            UpdRequest.UsePassive = true;
            UpdRequest.UseBinary = true;
            UpdRequest.KeepAlive = false;

            //load file
            //把檔案轉換成byte陣列的資料流
            FileStream fileStream = File.OpenRead(sourceFile);
            byte[] buffer = new byte[fileStream.Length];
            fileStream.Read(buffer, 0, buffer.Length);
            fileStream.Close();

            //開始上傳資料流
            Stream requestStream = UpdRequest.GetRequestStream();
            requestStream.Write(buffer, 0, buffer.Length); //從buffer中的第0個位元開始上傳到buffer.length這個地方
            requestStream.Close();
            success = true;

            ftpUploadResponse = (FtpWebResponse)UpdRequest.GetResponse(); //回覆訊息
            responseMsg = ftpUploadResponse.StatusDescription;
        }

這裡要注意放在FtpWebRequest中的伺服器位置要遁守URI-Uniform resource identifier規則。

Truncate資料表
假設已經存在一dataset, 現在要Truncate Dataset相關資料庫中的table, 最簡單的方法如下:
                        SqlCommand cmd = new SqlCommand(string.Format("TRUNCATE TABLE {0}", ds.Tables["DataSet資料表名稱"]), conn);
                        cmd.ExecuteNonQuery();


發送電子郵件
        public static void sendMail(string mailServerIP, string fromMail,  string toMail, string subject, string content )
        {
            try
            {
                //宣告一SMTP物件
                SmtpClient smtpclient = new SmtpClient();
                //SMTP伺服器IP
                smtpclient.Host = mailServerIP;
                //新增一mail
                MailMessage msgMail = new MailMessage();
                //加入收件者
                msgMail.To.Add(toMail);
                MailAddress ma = new MailAddress(fromMail);
                msgMail.From = ma;
                msgMail.Subject = subject;
                msgMail.Body = content;
                msgMail.IsBodyHtml = true;
                //send!
                smtpclient.Send(msgMail);
            }
            catch
            {
            }
        }



1. http://www.dotspace.idv.tw/Jyemii/umlcolumn/articles/umlwriting/UMLBasics/UMLBasics.htm UML入門:統一建模語言入門
2. http://www.wretch.cc/blog/smalltide/11664739 Introduce UML Class Diagram
3. http://msdn.microsoft.com/ MSDN.
4. http://www.vcskicks.com/csharp_ftp_upload.php C# FTP Upload

Popular posts from this blog

[SQL SERVER] 找出LOCK方法懶人包

[SQL Server] 解決log檔(ldf file)過度膨脹的實戰經驗

[Windows7] 跨距磁碟區, 等量磁碟區, 鏡像磁碟區之區別