MVC WebAPI 三層分佈式框架開發

前言:SOA(面向服務的架構)是目前企業應用開發過程中普遍採用的技術,基於MVC WebAPI三層分佈式框架開發,以此適用於企業信息系統的業務處理,是本文論述的重點。此外,插件技術的應用,富客戶端JQuery實現技術,本文也對其具體實現做以說明。相關示例解決方案可以參考GitHub資源,在文章結尾給出。


1. 系統分層體系架構設計

分佈式三層系統簡單分爲數據訪問層,業務邏輯層和前端展現層。分層架構設計是構建大型分佈式系統的必要手段,因爲可以使得系統健壯,可擴展。


SOA即面向服務的架構,可以幫助企業構建靈活,擴展性強的複雜業務系統,按照服務的理念進行功能交付,調用方也不用去知道實現一方的具體細節;雙方是按照統一消息格式,接口方式進行交互的。


SOA的實現是基於以Web服務的方式發佈Api接口,目前WebAPI是一種Restfule形式的Web服務,相比WCF的複雜性,WebAPI的開發效率更高,而且在配置時也不需要客戶端和服務端的xml配置。


企業核心業務數據可以讓桌面、Web、平板、手機或物聯設備訪問,所以需要統一API接口,WebApi作爲業務邏輯處理服務能夠滿足接口訪問和接口之間交互的需求。


2.基礎類庫模塊
2.1 數據訪問:Dapper-微型ORMapping框架
Dapper的優勢:
1,Dapper是一個輕型的ORM類。代碼就一個SqlMapper.cs文件,編譯後就40K的一個很小的Dll.
2,Dapper很快。Dapper的速度接近與IDataReader,取列表的數據超過了DataTable。
3,Dapper支持Mysql,SqlLite,Mssql,Oracle等一系列的數據庫,當然如果你知道原理也可以讓它支持Mongo db
4,Dapper的r支持多表並聯的對象。支持一對多 多對多的關係。並且沒侵入性,想用就用,不想用就不用。無XML無屬性。代碼以前怎麼寫現在還怎麼寫。
5,Dapper原理通過Emit反射IDataReader的序列隊列,來快速的得到和產生對象。性能實在高。
6,Dapper 是C#實現,支持.net framework 各種版本;

7,Dapper語法十分簡單。並且無須遷就數據庫的設計。

國外大型網站採用的有:
–StackOverflow, StackExcahnge等。。。

讀取500條記錄,並做簡單對象的序列化操作時間對比如下圖:


2.2 DataRepository

•實現數據實體操作封裝
-Insert—插入
-Update—更新
-Delete—刪除
-Select—選取
-Paged—分頁

2.3  ServiceBase
•實現業務實體對象的操作封裝
–Insert—插入
–Update—更新
–Delete—刪除
–Select—選取
–Paged—分頁

2.4 服務實現類

-實現Iservice接口
-繼承ServiceBase基類


2.5 WebAPI服務發佈

API Controller

    --[HttpGet]

    --[HttpPost]

    --[HttpPut]

    --[HttpDelete]



2.6 動態加載插件
-系統的擴展性
-系統的變化性
-客戶二次開發
-MEF
–運行時加載

2.7 AutoMapper實體對象之間轉換
•兩個實體類
–EPProduct – 數據實體
–Product– 業務實體
•轉化示例代碼
EPProduct p =ProductRepository.Get(long.Parse(id));
AutoMapper.Mapper.CreateMap<EPProduct, Product>();
Productentity =AutoMapper.Mapper.Map<EPProduct, Product>(p)

2.8 面向接口編程--Ioc框架

•SimpleInjector
–靜態類型
–編譯階段
•MEF
–動態類型
–運行時階段

3.富客戶端開發
3.1 Asp.NETMVC 富客戶端開發
•Model
–WebAPI (服務接口)
•Controller
–路由
•View
–頁面
•富客戶端
–Ajax 局部刷新
– 鼠標、鍵盤響應事件等
–如Gmail郵箱等應用示例

3.2 Jquery插件
•Layout—Jquery Layout
•DataGrid – SlickGrid –性能非常高
•Tree– Jstree/Ztree –評價都不錯
•Tab– Jquery Tools
•Toolbar– Jquery Tools
•Dialog– Jquery Tools
•Form–Jquery Tools

3.3 前端頁面Ajax調用:
GET/POST/PUT/DELETE
 /***
     * HttpGet獲取服務端數據
     * @url 業務數據
     * @data
     */
    $.doHttpClientGet = function(url, fn) {
        $.getJSON(url, fn);
    }


    /***
     * HttpPut更新數據到服務端
     * @url 業務數據
     * @data
     */
    $.doHttpClientUpdate = function(url, data, fn) {
        $.ajax({
            url: url,
            type: 'PUT',
            data: data,
            dataType: 'json',
            contentType: 'application/json',
            success: fn
        });
    }


    /***
     * HttpDelete刪除數據
     * @url 業務數據
     * @data
     */
    $.doHttpClientDelete = function(url, data, fn) {
        $.ajax({
            url: url,
            type: 'DELETE',
            data: data,
            dataType: 'json',
            contentType: 'application/json',
            success: fn
        });
    }


    /***
     * HttpPost保存數據
     * @url 業務數據
     * @data
     */
    $.doHttpClientSave = function(url, data, fn) {
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            dataType: 'json',
            contentType: 'application/json',
            success: fn
        });
    }


    /***
     * ajax獲取服務端數據
     * @url 業務數據
     * @data
     */
    $.doAjaxGet = function(url, fn) {
        //$.getJSON(url, fn);
        $.ajax({
            url: url,
            type: "GET",
            dataType: 'json',
            //data: data,
            contentType: 'application/json',
            success: fn
        });
    }


    $.doAjaxPost = function(url, data, fn) {
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            dataType: 'json',
            contentType: 'application/json',
            success: fn
        });
    }


    //構造html的通用方法
    $.buildHTML = function(tag, html, attrs) {
        // you can skip html param
        if (typeof (html) != 'string') {
            attrs = html;
            html = null;
        }
        var h = '<' + tag;
        for (attr in attrs) {
            if (attrs[attr] === false) continue;
            h += ' ' + attr + '="' + attrs[attr] + '"';
        }
        return h += html ? ">" + html + "</" + tag + ">" : "/>";
    }


    //構造JsTree的通用方法
    $.fn.buildJsTree = function (url, fn) {
        var object = require(['jstree'], function(){
            $.jstree._themes = "/PlatJS/Scripts/jstree/themes/";
            var myTree = $(this).jstree({
                "json_data": {
                    "ajax": {
                        "url": url,
                        "type": "GET",
                        "dataType": "json",
                        "contentType": "application/json charset=utf-8",
                        "success": fn
                    }
                },
                "plugins": ["themes", "json_data", "ui"]
            });
        })
    }

3.4 如何調試
•Fiddler--*****5star

FireBug for Firefox

•查看HTML,CSS,Javascript等
•監控下載圖片資源時間線
•完善友好的調試


FirefoxRestClient插件—Rest Client測試插件

http://localhost:8081/ProductSys.WebAPI/api/order/insertwith?type="insertwith

 [HttpPost]

public HttpResponseMessageInsertWith(Order entity, string type)



http://localhost:8081/ProductSys.WebAPI/api/order/4

 [HttpDelete]

 public HttpResponseMessage Delete(string id)




3.5 Web異常錯誤代碼

•100-199– Informational
•200-299– Client request successful
•300-399– Client request redirected, further action necessary
•400-499– Client request incomplete
•500-599– Server error

4. Javascript 類語法
4.1 常見問題
•Namespace(命名空間)
–默認爲全局範圍,有潛在類型衝突隱患
•SelfExecuting Fuction (自執行匿名函數)
•Objectand Array (對象和數組初始化)
–不要使用new 關鍵字
•NullOr Empty (檢查NULL)

4.2 Javascript-自執行匿名函數
//Self-Executing Anonymous Func: Part 2 (Public & Private)
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;
 
    //Public Property
    skillet.ingredient = "Bacon Strips";
     
    //Public Method
    skillet.fry = function() {
        var oliveOil;
         
        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };
     
    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }    
}( window.skillet = window.skillet || {}, jQuery ));
 <pre name="code" class="javascript">//Public Properties
console.log( skillet.ingredient ); //Bacon Strips
 
//Public Methods
skillet.fry(); //Adding Butter & Fraying Bacon Strips
 
//Adding a Public Property
skillet.quantity = "12";
console.log( skillet.quantity ); //12
 
//Adding New Functionality to the Skillet
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";
     
    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
    };    
}( window.skillet = window.skillet || {}, jQuery ));
 
try {
    //12 Bacon Strips & 1 Cup of Grease
    skillet.toString(); //Throws Exception
} catch( e ) {
    console.log( e.message ); //isHot is not defined
}
</pre>

4.3 對象和數組初始化
//建議申明對象或數組的寫法
var person = {}, 
    keys = [];

//申明覆雜對象或數組的寫法
var person = {
        firstName: "Elijah",
        lastName: "Manor",
        sayFullName: function() {
            console.log( this.firstName + " " + 
                this.lastName );
        }
    }, 
    keys = ["123", "676", "242", "4e3"];

4.4 判斷對象是否爲NULL(c#)
// <span style="color:#ff0000;">C# 例子. 不要在Javascript中這樣寫</span>
if ( someString != null &&
    someString.length > 0 ) {
    //Do something here...
}

// C# 例子 檢查字符串是否爲空
if ( !string.IsNullOrEmpty(someString) ) {
    //Do something here...
}

4.5 判斷對象是否爲NULL(javascript)
Javascript中的正確寫法
// Simplified JavaScript syntax to check for
// undefined, null, & empty string values
if ( someString ) {
    //Do something here...
}

4.6 設置缺省值(c#)

<span style="color: rgb(255, 0, 0);">// C# 例子,不要在Javascript這樣寫</span>
if ( someString == null ) {
   someString = "default Value";
}
// Slightly better, but don't do this either
someString = someString ? someString : "default value"; <pre name="code" class="javascript">請在Javascript按如下格式寫
// JavaScript syntax to set a default value
someString = someString || "default value";















4.7 不同類型的比較操作符(==, !=)
•// Unexpected Comparisons using the== Operator
0         ==  ''        //true
0         ==  '0'       //true
false     ==  '0'       //true
null      ==  undefined //true
'\t\r\n ' ==  0         //true


4.8 不同類型的比較操作符(===, !==)
•// Expected Comparisons using the ===Operator
0         === ''        //false
0         === '0'       //false
false     === '0'       //false
null      === undefined //false
'\t\r\n ' === 0         //false

4.9 不可取的數組遍歷操作符for…in
var myArray = [], name;
myArray[5] = "test";
console.log( myArray.length ); //6
 
for ( name in myArray ) {
    console.log( name, myArray[name] );
    //Outputs...
    //   5, test
}

4.10 正確的數組遍歷操作符for…;…;
var myArray = [], name;
myArray[5] = "test";
console.log( myArray.length ); //6
 
for ( var i = 0, length = myArray.length; i < length; i++ ) {
    console.log( i, myArray[i] );
    //Outputs...
    //   0, undefined
    //   1, undefined
    //   2, undefined
    //   3, undefined
    //   4, undefined
    //   5, test
}
for ( var name in object ) {
    //Your code here
}

/* Check if object has property before
iterating, because functions inherited
from prototype are also included */
for ( var name in object ) {
   if ( object.hasOwnProperty(name) ) {
      //Your code here
   }
}

5. RequireJS 模塊化腳本
RequireJS 是一個非常小巧的 JavaScript 模塊載入框架,是 AMD 規範最好的實現者之一。最新版本的 RequireJS 壓縮後只有 14K,堪稱非常輕量。它還同時可以和其他的框架協同工作,使用 RequireJS 必將使的前端代碼質量得以提升。

RequireJS 作爲 JavaScript 文件的加載器,還是可以完成異步非阻塞的文件加載。
define(['Controllers/Main/ListView'], function (ListView) {
    function start() {
        var users = JSON.parse(localStorage.users);
        ListView.render({ users: users });
    }

    return {
        start: start
    };
});


6. 網絡資源
6.1 NuGet快捷獲取軟件包
充分利用開源軟件包,避免重複製造輪子;
也可以自己發佈軟件包,回饋社區,先進技術的積累可以節約項目成本。

6.2 技術資源

•Asp.net MVC WebAPI
–RestfulWeb Service的發展
•Jquery
–官網
–插件開發
•RequrieJS
–Javascript模塊化開發框架
•Dapper
–微型ORMapping 框架
•EntityFramework
–Microsoft實體框架

7. 總結:
本文基於MVC WebAPI實現分佈式三層架構,實現了通用數據訪問操作,業務實體和數據實體的交互操作,業務模塊之間的接口交互;實現了插件化的加載技術。此外限於篇幅,對於流程化的設計,會在下文論述,主要會談及到工作流Api和WebApi的交互。


代碼示例說明:

提供的示例RequireMVC199中,可以看一下ProductSys.WebApi的服務層代碼,前端代碼看RequireMvc199的WebApplication項目即可。


完整示例,可以看一下ProductList頁面的代碼,這個示例是完整的,包括文件:

WebApplication 包括:

\Controllers

    --ProductController.cs

\ViewJS

  \Controllers

    \Product

       --product-list.js

      --product-detail.js


\Views

  \Product

    --productlist.cshtml


WebApi 包括:

ProductSys.WebApi

  \Controllers

    --ProductController.cs


ProductSys.ServiceImp

  \Service

    --ProductService.cs



解決方案下載地址:
https://github.com/besley/Plat2012

新版本下載:
http://github.com/besley/slickone

後記:
不斷有朋友問到,是否有新版本?是否支持ORACLE?目前在最新的版本里已經解決這些問題。你可以在Slickflow的項目裏面看到,即仍然是Mvc, WebApi和Dapper的架構,但是簡化甚多,真是物至簡方可盡其用,希望大家一起學習討論。謝謝大家關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章