.NET框架設計(常被忽視的C#設計技巧)
閱讀目錄:
- 1.開篇介紹
- 2.儘量使用Lambda匿名函式呼叫代替反射呼叫(走進宣告式設計)
- 3.被忽視的特性(Attribute)設計方式
- 4.擴充套件方法讓你的物件如虎添翼(要學會使用擴充套件方法的設計思想)
- 5.別怕Static屬性(很多人都怕Static在Service模式下的設計,其實要學會使用執行緒本地儲存(ThreadStatic))
- 6.泛型的協變與逆變(設計架構介面(Interface)時要時刻注意物件的協變、逆變)
- 7.使用泛型的型別推斷(還在為引數型別煩惱嗎)
- 8.鏈式程式設計(設計符合大腦思維習慣的處理流程)
- 8.1.鏈式程式設計(多條件(方法碎片化)呼叫
- 8.1.鏈式程式設計(多條件(方法碎片化)呼叫
- 9.部分類、部分方法的使用(擴大設計範圍)
1.】開篇介紹
本文中的內容都是我無意中發現覺得有必要分享一下的設計經驗,沒有什麼高深的技術,只是平時我們可能會忽視的一些設計技巧;為什麼有這種想法是因為之前跟一些同事交流技術的時候會發現很多設計思維被固化了,比如之前我在做客戶端框架開發的時候會去設計一些關於Validator、DTO Transfer等常用的Common function,但是發現在討論某一些技術實現的時候會被弄的雲裡霧裡的,會自我鬱悶半天,不會及時的明白對方在說的問題;
後來發現他們一是沒有把概念分清楚,比如.NETFramework、C#、VisualStudio
.NETFramework的版本不斷的在升級,目前差不多5.0都快面世了;.NETFramework的升級跟C#的升級沒有必然的關係,這個要搞清楚;C#是為了更好的與.NET平臺互動,它提供給我們的都是語法糖,最後都是.NETCTS中的型別;就比如大家都在寫著LINQ,其實到最後LINQ也就被自動解析成對方法的直接呼叫;
2.】儘量使用委託呼叫代替反射呼叫
委託相信大家都玩的很熟,委託的發展到目前為止是相當不錯的,從原本很繁瑣的每次使用委託的時候都需要定義一個相應的方法用來例項化委託,這點在後來的C#2中得到了改進,支援匿名委託delegate{…}的方式使用,再到現在的C#3那就更方便了,直接使用面向函式式的Lambda表示式;那麼這樣還需要反射呼叫物件的方法嗎?(當然特殊場合我們這裡不考慮,只考慮常用的場景;)當然反射不是不好,只是反射需要考慮很多效能優化方面的東西,增加了程式碼的複雜性,也讓框架變的很重(現在都是在追求輕量級,只有在DomainModel中需要將平面化的資料抽象;),所以何不使用簡單方便的委託呼叫呢;
注:如果你是初學者,這裡的委託可以理解成是我們平時常用的Lambda表示式,也可以將它與Expression<T>結合起來使用,Expression<T>是委託在執行時的資料結構,而非程式碼執行路徑;(興趣的朋友
下面我們來看一下演示程式碼:
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-07-28 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 using System; 8 using System.Collections.Generic; 9 using System.Linq.Expressions; 10 using System.Linq; 11 using Infrastructure.Common.Cache; 12 using Infrastructure.Common.Validator; 13 14 namespace ConsoleApplication1.DomainModel 15 { 16 /// <summary> 17 /// Order. 18 /// </summary> 19 [EntityCache(10, true)] 20 [EntityValidator(ValidatorOperationType.All)] 21 public class Order 22 { 23 /// <summary> 24 /// Order code. 25 /// </summary> 26 public string OrderCode { get; set; } 27 28 /// <summary> 29 /// Items filed. 30 /// </summary> 31 private List<Item> items = new List<Item>(); 32 /// <summary> 33 /// Gets items . 34 /// </summary> 35 public IEnumerable<Item> Items { get { return items; } } 36 37 /// <summary> 38 /// Submit order date. 39 /// </summary> 40 public DateTime SubmitDate { get; set; } 41 42 /// <summary> 43 /// Mark <see cref="DomainModel.Order"/> Instance. 44 /// </summary> 45 /// <param name="orderCode">Order code. </param> 46 public Order(string orderCode) 47 { 48 this.OrderCode = orderCode; 49 } 50 51 /// <summary> 52 /// Sum items prices. 53 /// </summary> 54 /// <param name="itemUsingType">item type.</param> 55 /// <returns>prices .</returns> 56 public double SumPrices(int itemUsingType) 57 { 58 double resultPrices = 0.00; 59 var currentItems = items.GroupBy(item => item.ItemUsingType).Single(group => group.Key == itemUsingType); 60 if (currentItems.Count() > 0) 61 { 62 foreach (var item in currentItems) 63 { 64 resultPrices += item.Price; 65 } 66 } 67 return resultPrices; 68 } 69 70 /// <summary> 71 /// Add item to order. 72 /// </summary> 73 /// <param name="item">Item.</param> 74 /// <returns>bool.</returns> 75 public bool AddItem(Item item) 76 { 77 if (!item.ItemCode.Equals(string.Empty)) 78 { 79 this.items.Add(item); 80 return true; 81 } 82 return false; 83 } 84 } 85 }View Code
這是一個訂單領域實體,它裡面引用了一個Item的商品型別;
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-07-28 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 using System; 8 9 namespace ConsoleApplication1.DomainModel 10 { 11 /// <summary> 12 /// Order item. 13 /// </summary> 14 public class Item 15 { 16 /// <summary> 17 /// Item code. 18 /// </summary> 19 public Guid ItemCode { get; set; } 20 21 /// <summary> 22 /// Item price. 23 /// </summary> 24 public float Price { get; set; } 25 26 /// <summary> 27 /// Item using type. 28 /// </summary> 29 public int ItemUsingType { get; set; } 30 } 31 }View Code
上面程式碼應該沒有問題,基本的訂單領域模型大家都太熟了;為了保證上面的程式碼是絕對的正確,以免程式錯誤造成閱讀者的不爽,所以都會有100%的單元測試覆蓋率;這裡我們主要使用的是Order類中的SumPrices方法,所以它的UnitTest是100%覆蓋;
圖1:
Order中的SumPrices方法的UnitTest程式碼:
1 using System; 2 using Microsoft.VisualStudio.TestTools.UnitTesting; 3 using NSubstitute; 4 5 namespace ConsoleApplication.UnitTest 6 { 7 using ConsoleApplication1.DomainModel; 8 9 /// <summary> 10 /// Order unit test. 11 /// </summary> 12 [TestClass] 13 public class DomainModelOrderUnitTest 14 { 15 /// <summary> 16 /// Order sumprices using type 1 test. 17 /// </summary> 18 [TestMethod] 19 public void DomainModelOrderUnitTest_SumPrices_ItemUsingTypeIs1_UnitTest() 20 { 21 Order testOrder = new Order(Guid.NewGuid().ToString()); 22 23 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 10.0F }); 24 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 15.0F }); 25 26 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 20.0F }); 27 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 30.0F }); 28 29 double result = testOrder.SumPrices(1); 30 31 Assert.AreEqual(result, 25.0F); 32 } 33 34 /// <summary> 35 /// Order sumprices using type is 2 test. 36 /// </summary> 37 [TestMethod] 38 public void DomainModelOrderUnitTest_SumPrices_ItemUsingTypeIs2_UnitTest() 39 { 40 Order testOrder = new Order(Guid.NewGuid().ToString()); 41 42 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 10.0F }); 43 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 15.0F }); 44 45 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 20.0F }); 46 testOrder.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 30.0F }); 47 48 double result = testOrder.SumPrices(2); 49 50 Assert.AreEqual(result, 50.0F); 51 } 52 } 53 }View Code
在以往我基本上不寫單元測試的,但是最近工作上基本上都需要寫每個方法的單元測試,而且要求是100%覆蓋,只有這樣才能保證程式碼的正確性;也建議大家以後多寫單元測試,確實很有好處,我們應該把單元測試做起來;下面我們言歸正傳;
由於我們的Order是在DomainModel Layer中,現在有一個需求就是在Infrastructure Layer 加入一個動態計算Order中指定Item.ItemUsingType的所有Prices的功能,其實也就是說需要將我們的一些關鍵資料通過這個功能傳送給遠端的Service之類的;這個功能是屬於Infrastructure中的Common部分也就是說它是完全獨立與專案的,在任何地方都可以通過它將DomainModel中的某些領域資料傳送出去,那麼這樣的需求也算是合情合理,這裡我是為了演示所以只在Order中加了一個SumPrices的方法,可能還會存在其他一些DomainModel物件,然後這些物件都有一些關鍵的業務資料需要在通過Infrastructure的時候將它們傳送出去,比如傳送給配送部門的Service Interface;
那麼常規設計可能需要將擴充套件點配置出來放在指定的配置檔案裡面,然後當物件經過Infrastructure Layer中的指定Component時觸發事件路由,然後從快取中讀取出配置的資訊執行,那麼配置檔案可能大概是這樣的一個結構:DomainEntity名稱、觸發動作、方法名稱、引數,DomainEntity名稱是確定聚合根,觸發動作是對應Infrastructure中的元件,當然你也可以放在DomainModel中;這裡只關心方法名稱、引數;
當然這裡只演示跟方法呼叫相關的程式碼,其他的不在程式碼中考慮;我們來看一下相關程式碼:
1 using System; 2 namespace Infrastructure.Common 3 { 4 public interface IBusinessService 5 { 6 void SendBusinessData(object callAuthor, string methodName, object parameterInstance); 7 void SendBusinessData<P>(Func<P, object> callFun, P parameter); 8 } 9 }View Code
這是業務呼叫介面;
1 using System; 2 using System.Reflection; 3 using System.Linq; 4 using System.Linq.Expressions; 5 6 namespace Infrastructure.Common 7 { 8 /// <summary> 9 /// Business service . 10 /// </summary> 11 public class BusinessService : IBusinessService 12 { 13 /// <summary> 14 /// Send service data interface . 15 /// </summary> 16 private ISendServiceData sendService; 17 18 /// <summary> 19 /// Mark <see cref="Infrastructure.Common.ISendServiceData"/> instance. 20 /// </summary> 21 /// <param name="sendService"></param> 22 public BusinessService(ISendServiceData sendService) 23 { 24 this.sendService = sendService; 25 } 26 27 /// <summary> 28 /// Send business data to service interface. 29 /// </summary> 30 /// <param name="callAuthor">Object author.</param> 31 /// <param name="methodName">Method name.</param> 32 /// <param name="parameterInstance">Method call parameter.</param> 33 public void SendBusinessData(object callAuthor, string methodName, object parameterInstance) 34 { 35 object result = 36 callAuthor.GetType().GetMethod(methodName).Invoke(callAuthor, new object[] { parameterInstance }); 37 if (result != null) 38 { 39 sendService.Send(result); 40 } 41 } 42 43 /// <summary> 44 /// Send business data to service interface. 45 /// </summary> 46 /// <typeparam name="P"></typeparam> 47 /// <param name="callFun"></param> 48 /// <param name="parameter"></param> 49 public void SendBusinessData<P>(Func<P, object> callFun, P parameter) 50 { 51 object result = callFun(parameter); 52 if (result != null) 53 { 54 sendService.Send(result); 55 } 56 } 57 } 58 }View Code
這裡簡單實現IBusinessService介面,其實程式碼很簡單,第一個方法使用反射的方式呼叫程式碼,而第二個方法則使用委託呼叫;在實現類裡面還包含了一個簡單的介面;
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Infrastructure.Common 8 { 9 public interface ISendServiceData 10 { 11 void Send(object sendObject); 12 } 13 }View Code
目的是為了方便單元測試,我們來看一下單元測試程式碼;
1 using System; 2 using Microsoft.VisualStudio.TestTools.UnitTesting; 3 using Infrastructure.Common; 4 using ConsoleApplication1.DomainModel; 5 using NSubstitute; 6 7 namespace Infrastructure.Common.UnitTest 8 { 9 [TestClass] 10 public class InfrastructureCommonBusinsessServiceUnitTest 11 { 12 [TestMethod] 13 public void InfrastructureCommonBusinsessServiceUnitTest_BusinessService_SendBusinessData() 14 { 15 Order order = new Order(Guid.NewGuid().ToString()); 16 17 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 10.0F }); 18 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 15.0F }); 19 20 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 20.0F }); 21 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 30.0F }); 22 23 ISendServiceData mockISendServiceData = Substitute.For<ISendServiceData>(); 24 object sendresult = null; 25 mockISendServiceData.When(isend => isend.Send(Arg.Any<object>())).Do(callinfo => sendresult = callinfo.Arg<object>()); 26 27 BusinessService testService = new BusinessService(mockISendServiceData); 28 testService.SendBusinessData(order, "SumPrices", 1); 29 30 Assert.AreEqual((double)sendresult, 25); 31 } 32 33 [TestMethod] 34 public void InfrastructureCommonBusinsessServiceUnitTest_BusinessService_SendBusinessDataGen() 35 { 36 Order order = new Order(Guid.NewGuid().ToString()); 37 38 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 10.0F }); 39 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 1, Price = 15.0F }); 40 41 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 20.0F }); 42 order.AddItem(new Item() { ItemCode = Guid.NewGuid(), ItemUsingType = 2, Price = 30.0F }); 43 44 ISendServiceData mockISendServiceData = Substitute.For<ISendServiceData>(); 45 object sendresult = null; 46 mockISendServiceData.When(isend => isend.Send(Arg.Any<object>())).Do(callinfo => sendresult = callinfo.Arg<object>()); 47 48 BusinessService testService = new BusinessService(mockISendServiceData); 49 testService.SendBusinessData<Order>(ord => { return ord.SumPrices(1); }, order); 50 51 Assert.AreEqual((double)sendresult, 25); 52 } 53 } 54 }View Code
在第二個單元測試方法裡面我們將使用Lambda方式將邏輯直接注入進BusinessService中,好就好這裡;可以將Lambda封進Expression<T>然後直接儲存在Cache中或者配置中間,徹底告別反射呼叫吧,就好比委託一樣沒有人會在使用委託在定義個沒用的方法;(所以函數語言程式設計越來越討人喜歡了,可以關注一下F#;)總之使用泛型解決型別不確定問題,使用Lambda解決程式碼邏輯注入;大膽的嘗試吧,將宣告與實現徹底分離;
(對.NET單元測試有興趣的朋友後面一篇文章會詳細的講解一下如何做單元測試,包括Mock框架的使用;)
3】被忽視的特性(Attribute)設計方式
大部分人對特性的定義是程式碼的“資料註釋”,就是可以在執行時讀取這個特性用來做型別的附加屬性用的;通常在一些框架中對DomainModel中的Entity進行邏輯上的關聯用的,比如我們比較熟悉的ORM,都會在Entity的上面加上一個類似 [Table(TableName=”Order”)] 這樣的特性宣告,然後再在自己的框架中通過反射的方式去在執行時差找元資料找到這個特性,然後就可以對附加了這個特性的型別進行相關的處理;
這其實沒有問題,很正常的設計思路,也是比較通用的設計方法;但是我們的思維被前人固化了,難道特性就只能作為程式碼的宣告嗎?問過自己這個問題嗎?
我們繼續使用上面2】小結中的程式碼作為本節演示程式碼,現在我們假設需要在DomainModel中的Entity上面加上兩個特性第一個用來斷定它是否需要做Cache,第二個用來確定關於Entity操作驗證的特性;
看程式碼:
1 /// <summary> 2 /// Order. 3 /// </summary> 4 [EntityCache(10, true)] 5 [EntityValidator(ValidatorOperationType.All)] 6 public class Order 7 {}View Code
程式碼應該很明瞭,第一EntityCache用來設計實體的快取,引數是快取的過期時間;第二個特性EntityValidator用來設定當實體進行相關處理的時候需要的驗證型別,這裡選擇是所有操作;
現在的問題是關於特性的優先順序,對於Order類的處理到底是先Cache然後驗證,還是先驗證然後Cache或者說內部沒有進行任何的邏輯處理;如果我們將特性的視為程式碼的標識而不是真正的邏輯,那麼對於優先順序的處理會比較棘手,你需要設計如何將不同的特性處理邏輯關聯起來;比較合理的設計方法是特性的處理連結串列;本人之前設計過AOP的簡單框架,就遇到過對於特性的優先順序的處理經驗,也是用的連結串列的方式將所有的特性按照順序串聯起來然後將物件穿過特性內部邏輯,這也符合DDD的中心思想;
下面我們來看程式碼:
1 Codeusing System; 2 3 namespace Infrastructure.Common 4 { 5 [AttributeUsage(AttributeTargets.Class)] 6 public abstract class EntityOperation : Attribute 7 { 8 protected EntityOperation NextOperation { get; set; } 9 } 10 }View Code
我們抽象出所有的處理,然後在內部包含下一個處理邏輯的特性例項;然後讓各自的Attribute繼承自它;
相關推薦
.NET框架設計(常被忽視的C#設計技巧)
閱讀目錄: 1.開篇介紹 2.儘量使用Lambda匿名函式呼叫代替反射呼叫(走進宣告式設計) 3.被忽視的特性(Attribute)設計方式 4.擴充套件方法讓你的物件如虎添翼(要學會使用擴充套件方法的設計思想) 5.別怕Static屬性(很多人都怕Static在Service模式下的設計,其實
.NET框架設計(常被忽視的框架設計技巧)
閱讀目錄: 1.開篇介紹 2.元資料快取池模式(在執行時構造元資料快取池) 2.1.元資料設計模式(抽象出對資料的描述資料) 2.2.藉助Dynamic來改變IOC、AOP動態繫結的問題 2.3.元資料和模型繫結、元資料應該隱藏在Model背後、元資料與DSL的關係 3.鏈式配置D
C++ 類中特殊的成員變數(常變數、引用、靜態)的初始化方法
有些成員變數的資料型別比較特別,它們的初始化方式也和普通資料型別的成員變數有所不同。這些特殊的型別的成員變數包括: a.引用 b.常量 c.靜態 d.靜態常量(整型) e.靜態常量(非整型) 常量和引用,必須通過引數列表進行初始化。 靜態成員變
循環語句總結(代碼以C#為例)
bre else col 運行 循環條件 span 表達式 條件 ons 1. while循環 代碼格式: while(循環條件) { //循環體 } 流程圖: 解讀: 如果循環條件為真,則執行循環體執行完循環體後,再判斷條件是否為真如果為真,再執行循環體然後
機器學習系統設計(Building Machine Learning Systems with Python)- Willi Richert Luis Pedro Coelho
切分 秘密 閾值 isa 占用 第二版 思考 並且 了解 機器學習系統設計(Building Machine Learning Systems with Python)- Willi Richert Luis Pedro Coelho 總述 本書是 2014 的,看完以後才
SPI驅動框架-1(DM8127 Linux2.6.37為例)
orm span remove mac 設備 single 隊列 drive for 一、驅動程序結構 1、platform_device 文件:/arch/arm/mach-omap2/device.c static struct omap2_mcspi_platfor
android mvp高速開發框架介紹(dileber使用之圖片下載工具)
net com 例如 下載 pop bug span 介紹 launcher 這幾天忙著工作~ 今天抽時間又把框架的bug處理了一下~~並且把volley的源代碼改
鏈表的操作(自己實現的-c語言版)
自己實現 main clas 一個 class node ret scan bsp 描述:pass #include<stdio.h> #include <stdlib.h> typedef struct _node{ int v
數據庫表設計(一對多,多對多)
關系 log 一個 數據庫 inf 對應關系 分享圖片 也有 通過 做一個項目,必然是少不了數據庫設計的!在學習階段,基本都是單表。然而在實際開發過程中,一對多,多對多的表處處都是!簡單整理一下,一對多,多對多表如何設計整理一下思路: 數據庫
qt 時鐘設計(隨介面大小變動自適應)
demo連結:https://download.csdn.net/download/weixin_39841821/10568854 一、 需求 a) 畫出一個圓形錶盤,包含三角形的時針、分針、秒針,以及上方的刻度,加上名字 b) 在窗體發生變化的時候,時鐘能夠根據窗體大小自動調整
BBS--功能4:個人站點頁面設計(ORM跨表與分組查詢)
.html closed have god trunc font .cn userinfo mon 查詢: 日期歸檔查詢 1 date_format ============date,time,datetime=========
Eureka Server設計(轉載 石杉的架構筆記)
目錄: 一、問題起源 二、Eureka Server設計精妙的登錄檔儲存結構 三、Eureka Server端優秀的多級快取機制 四、總結 一、問題起源 Spring Cloud架構體系中,Eureka是一個至關重要的元件,它扮演
Xcode簡明教程(使用Xcode編寫C語言程式)
原文地址::http://c.biancheng.net/view/476.html 相關文章 1、IOS開發:Xcode入門開發第一個HelloWorld程式----https://jingyan.baidu.com/article/a17d5285c9afc48099c
go語言幾個最快最好運用最廣的web框架比較(大多數人不瞭解的特性)
令人敬畏的Web框架 如果你為自己設計一個小應用程式,你可能不需要一個Web框架,但如果你正在進行生產,那麼你肯定需要一個,一個好的應用程式。 雖然您認為自己擁有必要的知識和經驗,但您是否願意自行編寫所有這些功能的程式碼? 您是否有時間找到生產級外部包來完成這項工作? 您確定這將與您應用的其餘部分保持一致嗎?
十分鐘讓你明白Objective-C的語法(和Java、C++的對比)
很多想開發iOS,或者正在開發iOS的程式設計師以前都做過Java或者C++,當第一次看到Objective-C的程式碼時都會頭疼,Objective-C的程式碼在語法上和Java, C++有著很大的區別,有的同學會感覺像是看天書一樣。不過,語言都是相通的,有很多共性。下面列
資料倉庫結構設計(星型結構和雪花結構)
當有一個或多個維表沒有直接連線到事實表上,而是通過其他維表連線到事實表上時,其圖解就像多個雪花連線在一起,故稱雪花模型。雪花模型是對星型模型的擴充套件。它對星型模型的維表進一步層次化,原有的各維表可能被擴充套件為小的事實表,形成一些區域性的 " 層次 " 區域,這些被分解的表都連線到主維度表而不是事實表。如圖
Linux關於socket(TCP協議實現C/S結構)
socket概述 為了簡化開發通訊程式的工作,由Berkely學校開發了一套網路通訊程式的API函式標準 socket標準被擴充套件成window socket和unix socket linux中的網路程式設計通過socket介面實現。Socket既是一種特殊的IO,它也是一種檔案描述符。一個
.NET windows服務(一:創建windows服務)
ati 如何 安裝程序 log 寫入 註意 pat 名稱 請求 引用地址:https://docs.microsoft.com/zh-cn/dotnet/framework/windows-services/how-to-create-windows-services
.NET/ASP.NET Routing路由(深入解析路由系統架構原理)
閱讀目錄: 1.開篇介紹 2.ASP.NET Routing 路由物件模型的位置 3.ASP.NET Routing 路由物件模型的入口 4.ASP.NET Routing 路由物件模型的內部結構 4.1UrlRoutingModule 物件內部結構 4.2RouteBase、Route、
SSM框架整合(IntellIj IDEA+Maven+Spring+SpringMVC+MyBatis)之MyBatis
我認為框架整合不熟練的話按照MyBatis->SpringMVC->Spring順序整合比較好,先配置MyBatis是因為不需要額外的配置伺服器,進行單元測試比較容易。Spring是用來進行整合的,所以等其它框架配置好之後進行整合不會顯得很亂。