程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> 一個中型OA系統的架構過程

一個中型OA系統的架構過程

編輯:.NET實例教程

最近在作一圖(GIS)文(MIS)一體化辦公系統的架構,在這裡將其架構設計寫出來,供大家板磚板磚。系統采用DotNet作為其開發語言,C/S結構,分多層開發模式。以下部分為系統在進行詳細分析前所作的架構。

 一、大框架

   三層結構,界面層,邏輯層,數據層。其中界面層命名為:UI,邏輯層又分為幾層,業務外觀(BusinessFacadeProjects)、業務規則(BusinessRuleProjects)、數據訪問(DataAccessProjects)三層,其實算起來,系統已經不是嚴格的三層模式了,已經算作是多層系統了。

  設計思想說明:界面層與業務外觀層進入數據交互,業務外觀允許對業務規則或者數據訪問層進行數據交互,業務規則進允許對數據訪問層進行交互。也就是說用戶所對界面進行的所有操作,都先通過業務外觀層進入,不管邏輯層如何處理,最後都是通過數據訪問層與數據庫進行交互,同理,不管要從數據庫取任何數據,先通過數據訪問層再經過邏輯層的處理,再從業務外觀層“丟”給界面層。為何如此設計?多層架構思想在這裡就不多說了,本人在這裡考慮的是C/S與B/S的統一,各種數據庫的統一,例如,如果我的系統是C/S,我想改為B/S,那麼由於有業務外觀層統一為數據“入口”,那麼我要改的東西僅僅是添加B/S界面層的代碼;又,如果你把你的數據庫從SQL Server改為Oracle,那麼你只要導入數據庫,然後改一下數據訪問層的代碼,就可以了,當然了,這裡的前提是你的所有Sql語句都得寫在數據訪問層上。

 二、層架設

  1.界面層:這個沒有什麼好說的,UI就是一個項目(工程?包?)管它怎麼叫,反正編譯出來就一Exe文件。

  2.業務外觀:為了方便起見,在引用的時候只要引用一個則可,所以就一個工程BusinessFacade,當然裡面你可以包含多個類,然後在UI裡面using或者Import。

  3.業務規則:這裡分多個工程,一個為數據處理,一個為工作流引擎,一個為字符處理,另一個為項目規則定義。
  數據處理我們命名為DataOption,這裡的意思是只要是與數據有關的,且要進行相應處理的,我們就先經過這裡處理一下。
  工作流引擎命名為WorkFlow,由於太大塊,也太復雜,有時間再分出來討論。
  字符處理在這裡命名為StringFormat,這一個工程是用來干什麼的呢?其實很簡單的,比如將一些Sql語句拆散了再組合,又或者將一些字符串經過一些特定的處理再返回,這些東西在大系統裡面經常用到,所以在這裡把它分出來,另外編譯成一Dll,也方便以後重用。當然在後面的開發中,要求程序員要有正則表達式的相關知識。
  項目規則命名為ProjectRule,又是什麼東東呢?這裡只要是對一些數據進行標准化的處理規則,比如,我們要生成一個列表頁,一般是返回一個DataTable,然後綁定到DataGrid中,當然,如果你僅僅有DataTable,在UI上你就分不清每列代表什麼東西,除非你的DataTable命名非常規范。所以在這裡你還得返回一個Title(列頭組合),為了美觀,你當然還得返回Width(列寬)的集合,然後在UI裡地這些東西進行處理,讓用戶看起來是一個可讀的且美觀的列表。這些東西既然都是通用的,你為什麼不把他們再定義呢?也就是說把Title,Width,DataTable定義為一個類,比如定義為DataList,裡面包括一DataTable,兩個字符串數組,我們以這個類為標准進行數據交互,那麼UI層的開發人員,你就不用管別的層是如何開發的,總之你的處理就是按這個標准進行開發,同時,數據處理層的人員也是,不管你UI層如何搞,我只按這個標准生成數據集合,如此一來整個系統的數據列表的開發就統一起來了。同時由於有了這個標准,每一層的開發人員都最大限度地考慮了在這個標准的基礎上可能出現的錯誤處理,可以使整個系統容錯性更好更健壯。當然在這裡不僅僅是一個DataList,還要分很多的“Rule”,只是在這裡暫時就不一一列出了。

  4.數據訪問:分兩個工程,一個工程只對數據庫進行直接的操作,提供各種各樣的接口,我把它命名為DataBaseOption。另一個不進行對數據的直接操作,只是所有的與Sql語句有關的都放在這裡,然後通過DataBaSEOption進行操作數據庫,我在這裡命名為DataAccess。把這一層分離出來的好處是,如果客戶突然間想把系統改為另一種數據庫進行數據的存儲,那麼你只要改DataAccess的代碼及DataBaSEOption的少量代碼則可全盤搞定。

 三、層詳細架設

   1.UI界面層:界面層大概主要由表單,菜單,按鈕等組成,當然這些東西還有統一的樣式。然後用戶對數據的操作能做的大體就是添加刪除修改等功能,由於是在詳細分析前,所以當然別的不通用的功能在這裡暫時先不討論。由於以上部分是通用的,所以在這裡我們先做一個基類,在這裡,我們命名基類時前面帶Base字樣,一般的Form我們前面帶Frm,這個基類的命名則為BaseForm.vb/BaseForm.cs,它主要的功能是定義通用的數據操作及界面樣式生成,例如此系統要實現類似於Office的樣式功能,

且從2000-2003是可選擇的,那麼這些處理的方法就定義在這個Form裡面,當用戶改變樣式時,它的任務是對每一個MDI子窗口的樣式改為原先定義好的樣式。所以,在這裡我們得定義一個void或者sub類型的方法:

  public void SystemStyle(enum Office)
  {
    這裡包括窗體框架的樣式和窗體內輸入表單的樣式。
  }

  Office枚舉為2000/XP/2003/default,至於方法中如何處理,我們在這裡暫時不管它,留給編碼者思考。   為了在MDI子窗體中能實現統一的刪除/修改/添加功能,在這裡我們在基類裡面定義添加/編輯/刪除三個方法,且這三個方法在繼承後是可以override的,也就是說,為了防止在父窗體中點擊添加/修改/刪除按鈕都不會出問題,我們必須所有的子窗口都應該從這裡繼承,為了通用起見,在這裡的BaseForm中必須是沒有操作的,也就是說僅僅是一個空的方法體。 

    /// <summary>
    /// 保存,可重寫
    /// </summary>
    public virtual void Save()
    {
    }

    /// <summary>
    /// 刪除,可重寫
    /// </summary>
    public virtual void Delete()
    {
    }

    /// <summary>
    /// 添加新的東東
    /// </summary>
    public virtual void AddNew()
    {
    }

  在這個系統中,我所定義的BaseForm.cs原則是所有的窗體都應該繼承的基類,但是所有的窗體中又可以再分出類來,所以在這個基礎上,我們得再寫基類。比如,在系統中,輸入表單的Form占了系統的很大一部分,所以,在這裡我得給它們抽象出一個比較通用的操作方法,所以在這裡定義了一個BaseInputForm.cs,這個類用來干什麼呢?我在這裡的設想是只要開發人員在開發輸入窗體時,主要繼承了這個類,那麼他的工作就是設置一個TableName屬性為相對應的數據表名,把須要的控件拖進窗體,把控件的Name屬性設置為相應的字段名,則可實現數據的添加/更新/刪除。為了能達到這個功能,在這裡先繼承了BaseForm.cs,然後定義一個Hashtable,key用來保存輸入表單的Name屬性值,Value則保存表單的Text/Value值。
  一個Hashtable,我們如何才能把當前窗體的輸入表單值全部保存進去呢?這個就得寫一個遞歸的循環當前窗體的所有控件的方法了。因為窗體的控件不僅僅是可以輸入的,所以在這裡還得寫方法篩選,代碼如何寫,在這裡就不給出了。   在這裡,TableName是一個屬性,這個屬性我們用來保存當前被操作的數據表的表名,同時我們還得到了一個帶有字段名及對應該輸入值的Hashtable,所以在這裡我們得在業務規則(BusinessRuleProjects)層寫一個規則,這個規則包括一string字段(表名),一Hashtable字段(字段及對應值),一enum字段(添加/修改/刪除),然後我們的各層就通過這個規則作數據交互。例如,點擊保存按鈕->Save()->構造規則->調用相應該的數據訪問層的方法,實現數據更新。
  這個Form除了定義表單外,我們還得定義按鈕(Menu,Bar等)的自動生成,在這裡如果想實現與權限的對應,就得把按鈕的信息寫在數據庫裡,然後根據不同的登錄名不同的權限,生成不同的對應該操作菜單,在這裡由於時間限制,不作詳細討論了。
  有了以上的東東之後,那麼我們的程序員在設計輸入表單的時候,主要實現排版及設置好Name屬性後就一切OK,則於BaseInputForm.cs繼承了BaseForm.cs,所以在這裡程序員根本就不用考慮樣式,會自動處理(當然你的控件要支持多樣式功能,VS自帶的是不支持的,所以你得找一套支持的,如果你的水平夠強,自己寫一套也可,我在這裡的C/S系統就是用的一套國外開發的,而B/S的則是本人自己開發)。也不用考慮如何添加進數據庫的問題,因為這些問題不是屬於界面層的問題,至於此部分的設計,以後有時間再作分析。
  除了輸入表單的基類外,我們還考慮了一個列表頁的基礎,這個基類在這裡不作詳細說明了,其規則為上面ProjectRule的例子說明。

(明天要出差去了,以下部分不知何時才有時間補上。

由於“你的磚頭就是我的動力!”,所以,如果磚頭來得比較猛烈的話,估計小夢在出差期間只好再“砌磚”了)。

接上文,我們講解了UI界面層的在設計初期的架構部分,由於是在系統分析還沒有全面展開的情況下先做的初期架構,所以中間業務邏輯部分我們暫時放下,先來考慮數據訪問層的一些問題。

    2.數據訪問層:分兩個工程,一工程主要封裝數據庫訪問及操作的對像,我將其命名為DataBaseOption,另一工程主要功能為負責業務邏輯對操作數據庫部分的功能進行構造一些操作數據庫的規則然後調用DataBaseOption的對像進行對數據庫的操作,在這裡我把它命名為DataAccess。其中DataAccess與業務邏輯有關,在這裡暫時先放一邊,而DataBaSEOption我們可以看作僅僅與數據庫有關,所以在這裡我們先作這一部分的討論。

    對於數據庫的操作,我們最常用的就是查詢/插入/更新/刪除,當然對於一個OA系統來說,還應該多加一個,那就是事務。

    對於查詢,我們在這裡應該考慮的有直接傳入SQL語句查詢或者存儲過程的查詢,當然視圖在這裡你已經可以把它當一個表看待了,暫時不考慮。查詢返回的值,對於DotNet來說,最常用的可能就是返回DataSet,DataReader了,而DataTable被包含於DataSet之中。

    於對插入和更新,其實我們可以放在一起考慮的,因為插入本身就是更新了數據庫,而插入或者更新的方法當然可以用SQL語句實現,這些通用的方法在這裡我們還是得考慮的。再而就是用數據集更新數據庫,也就是用一個適配器更新一個記錄集(DataSet)。當然在更新記錄集當中我們也可以對記錄集裡面的一些記錄進行刪除,然後用記錄集更新數據庫,達到刪除數據的目的。

    至於刪除,就沒有什麼好說的了,刪除比較簡單。
    對於事務,能夠創建/結束與回滾。

    好了,有了上面的分析,現在我們開始思考如何架設這個類。首先當然得有個數據庫連接,為了嚴格地不讓這個連接在別的地方使用,我們定義為:

    private OracleConnection conn = new OracleConnection;
    private string connString;

    為了能實現DataSet更新數據庫,我們還得定義一個OracleDataAdapter:
    private OracleDataAdapter upDateAdaper;

    同時我們還得定義一個連接字符串,這個串,為了方便我們的開發,把它定義為一個屬性,便於我們把它存放於任何地方,比如存放在Web.config配置文件裡。
  public string ConnString
  {
   get
   {
    return connString;
   }
   set
   {
    connString = value;
   }
  }
 
   好了,到了這裡,我們得考慮性能的問題,如果一個連接長期連接的話,會占用很多的資源,所以在這裡我們要保證在對數據進行操作的時候連接是打開著的,同時,當我們的操作結束的時候,連接馬上關閉。如何實現呢?在這裡可以定義一個數據庫連接的方法,判斷conn的State返回或者從新打開一個連接。當我們用完這個連接後,記得Close()一下就可以了。

    查詢返回值問題,我們定義兩類,一類是DataSet,一類是OracelDataReader。
    public DataSet GetDataSet(string sql)
    {
    }
    public DataSet GetDataSet(string tableName,string fIElds,string condition)
    {
    }


    public static DataSet ReturnDataSet(……)
    ……

    public OracleDataReader GetDataReader(string sql)
    {
    }
    public OracleDataReader GetDataReader(……)
    {
    }
    public static OracleDataReader ReturnDataReader(……)
    {
    }
    ……

    由上面給出的部分代碼,我們可以看到有部分是static類型的,定義為static類型主要是為了方便引用,而僅僅為public類型DataSet方法,則要考慮到更新數據庫的問題,也就是說生成一個DataSet的同時要生成一個Adapter,在後面部分的更新程序裡面要保證這個在邏輯層已經被更改過的DataSet能夠Update到數據庫裡,當然這些說明應該在方法體上面的XML注釋裡說明。

    在這裡我們應該注意一點,也就是僅有一個sql參數及有三個參數的GetDataSet的區別,這兩個東東的作用幾乎是一樣的,為什麼寫兩個呢?這個是考慮到數據庫的問題,也就是說,如果你的系統在開發階段控制得很好,所有SQL語句全由DataBaseOption構造,那麼,如果你把你的數據庫從Oracle改為SQL Server的時候,可能你只要改變這個數據庫操作類就可以實現了;但是如果你的sql語句由DataAccess構造,然後傳給只有一個參數的GetDataSet,由於每種數據盡管都支持標准的SQL,但是也有些差別,所以當你進行數據庫移植時,有可能得同時修改DataAccess,DataBaSEOption的代碼。當然了,如果你的系統根本就不會出現這種改變數據庫容器的問題,你根本就不用考慮這麼多。

    設計了數據的查詢後,我們來思考一下數據的更新問題,前面說到了,有用SQL語句更新和DataSet更新,在這裡特定於這個系統來說,我們在第一部分還說到了用Hashtable保存數據然後更新的問題,所以在這裡我們還得考慮如果用Hashtable更新DataSet,然後再更新數據庫。廢話少說。

    public enum UpdateOption{insert,update};
    public int UpdateData(string sql)
    {
    }
    public int UpdateData(DataSet myDataSet)
    {
    }
    public int UpdateData(string tableName,Hashtable hsTable,UpdateOption option)
    {
    }
    public int UpdateData(……)
    {
    }

    同樣,在這裡也應該有static類型的。
    ……

    在這裡,傳入一個DataSet如果更新數據庫呢?首先在生成DataSet的時候我們要new一個DataAdapter,然後把這個Adapter暫時寄存於upDateAdaper中,在UpdateData裡面再調用upDateAdaper來update這個DataSet,至於代碼如何編寫,有什麼技巧性的問題,這個已經屬於編碼者的問題了。同樣像OracleCommand這些東西,由於僅僅與編碼者有關,在這裡不作分析。

    數據的刪除問題,一部分已經在數據更新裡面實現,另一部分就是用Sql語句實現,這些不用多說。

比如寫一些通用的ExecuteNonQuery方法則可。

    事務問題,其實在大型的數據庫裡都已經有事務處理功能了,我們的任務就是寫通用的方法創建/結束/回滾事務,方便在DataAccess裡面調用。

    由於小夢在出差,時間比較緊,在這裡就不能作很詳細的介紹了,其實對於數據庫操作類,僅僅是那些方法體的參數,都得考慮半天,真正在作架構的時候,你必須得考慮得全面,因為這個關系到你架構邏輯層引用的問題。同時,你必須要很詳細的注明每一個方法體的功能及其參數的說明,否則編碼人員不知道你的意圖如何,到時候就有可能出現偏差。如果你懂Rose(Rose不支持DotNet,要用Rational XDE Developer for .Net)用它來架構然後生成代碼,會節省你很多的時間。

    有了界面層和數據操作層,我們舉個例子說明它的工作過程:
    首先,我們的程序員對照著數據庫設計文檔設計一個Form(拖動輸入控件設置Name屬性)-->生成一輸入窗體-->點擊保存-->傳表名及帶有表信息的哈希表給業務外觀層-->調用DataAccess相應的類及方法-->調用DataBaSEOption的UpdateData(string tableName,Hashtable hsTable,UpdateOption option)方法,實現數據的插入/更數。 



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved