本篇将要和大家分享的是webapi中如何使用依賴注入,依賴注入這個東西在接口中常用,實際工作中也用的比較頻繁,是以這裡分享兩種在api中依賴注入的方式Ninject和Unity;由于快過年這段時間打算了解下vue.js,是以後面對webapi的分享文章可能會慢點更新,希望支援的朋友們多多諒解,畢竟隻有不斷充電學習,才能更好的适應it行業吧;本章内容希望大家喜歡,也希望各位多多掃碼支援和推薦謝謝:
» Task并行任務抓取部落格園首頁資訊
» IOC架構Ninject的使用
» IOC架構Unity的使用
下面一步一個腳印的來分享:
首先,咋們需要建立一個部落格資訊實體類 MoBlog ,實體類代碼如下:
1 public class MoBlog
2 {
3
4 public MoBlog() { }
5
6 /// <summary>
7 /// 作者昵稱
8 /// </summary>
9 public string NickName { get; set; }
10
11 /// <summary>
12 /// 标題
13 /// </summary>
14 public string Title { get; set; }
15
16 /// <summary>
17 ///該篇文字位址
18 /// </summary>
19 public string Url { get; set; }
20
21 /// <summary>
22 /// 描述
23 /// </summary>
24 public string Des { get; set; }
25
26 /// <summary>
27 /// 頭像圖檔位址
28 /// </summary>
29 public string HeadUrl { get; set; }
30
31 /// <summary>
32 /// 部落格位址
33 /// </summary>
34 public string BlogUrl { get; set; }
35
36 /// <summary>
37 /// 點贊次數
38 /// </summary>
39 public int ZanNum { get; set; }
40
41 /// <summary>
42 /// 閱讀次數
43 /// </summary>
44 public int ReadNum { get; set; }
45
46 /// <summary>
47 /// 評論次數
48 /// </summary>
49 public int CommiteNum { get; set; }
50
51 /// <summary>
52 /// 建立時間
53 /// </summary>
54 public DateTime CreateTime { get; set; }
55 }
View Code
然後,需要建立一個接口 IBlogsReposity ,并且定義一個如下代碼的方法:
1 public interface IBlogsReposity
2 {
3 /// <summary>
4 /// 擷取部落格資訊
5 /// </summary>
6 /// <param name="nTask"></param>
7 /// <returns></returns>
8 Task<IEnumerable<MoBlog>> GetBlogs(int nTask);
9 }
注意這裡定義的傳回類型是Task<T>,主要作用是async異步傳回部落格資訊,并且友善使用并行方式抓取不同頁數的資料,是以這裡傳遞了一個int類型的參數nTask(表示任務數量);好了咋們來一起看下具體實作接口的 BoKeYuan 類裡面的代碼:
public class BoKeYuan : IBlogsReposity
{
public async Task<IEnumerable<MoBlog>> GetBlogs(int nTask)
{
var blogs = new List<MoBlog>();
try
{
//開啟nTask個任務,讀取前nTask頁資訊
Task<IEnumerable<MoBlog>>[] tasks = new Task<IEnumerable<MoBlog>>[nTask];
for (int i = 1; i <= tasks.Length; i++)
{
tasks[i - 1] = await Task.Factory.StartNew<Task<IEnumerable<MoBlog>>>((page) =>
{
return GetBlogsByPage(Convert.ToInt32(page));
}, i);
}
//30s等待
Task.WaitAll(tasks, TimeSpan.FromSeconds(30));
foreach (var item in tasks.Where(b => b.IsCompleted))
{
blogs.AddRange(item.Result);
}
}
catch (Exception ex)
{
}
return blogs.OrderByDescending(b => b.CreateTime);
}
/// <summary>
///
/// </summary>
/// <param name="nPage">頁數</param>
/// <returns></returns>
async Task<IEnumerable<MoBlog>> GetBlogsByPage(int nPage)
{
var blogs = new List<MoBlog>();
try
{
var strBlogs = string.Empty;
using (HttpClient client = new HttpClient())
{
strBlogs = await client.GetStringAsync("http://www.cnblogs.com/sitehome/p/" + nPage);
}
if (string.IsNullOrWhiteSpace(strBlogs)) { return blogs; }
var matches = Regex.Matches(strBlogs, "diggnum\"[^>]+>(?<hzan>\\d+)[^:]+(?<burl>http[^\"]+)[^>]+>(?<title>[^<]+)<\\/a>[^=]+=[^=]+=\"(?<hurl>http://(\\w|\\.|\\/)+)[^>]+>[^\\/]+\\/\\/(?<hphoto>[^\"]+)[^<]+<\\/a>(?<bdes>[^<]+)[^\"]+[^=]+=[^>]+>(?<hname>[^<]+)[^2]+(?<bcreatetime>[^<]+)[^\\(]+\\((?<bcomment>\\d+)[^\\(]+\\((?<bread>\\d+)");
if (matches.Count <= 0) { return blogs; }
foreach (Match item in matches)
{
blogs.Add(new MoBlog
{
Title = item.Groups["title"].Value.Trim(),
NickName = item.Groups["hname"].Value.Trim(),
Des = item.Groups["bdes"].Value.Trim(),
ZanNum = Convert.ToInt32(item.Groups["hzan"].Value.Trim()),
ReadNum = Convert.ToInt32(item.Groups["bread"].Value.Trim()),
CommiteNum = Convert.ToInt32(item.Groups["bcomment"].Value.Trim()),
CreateTime = Convert.ToDateTime(item.Groups["bcreatetime"].Value.Trim()),
HeadUrl = "http://" + item.Groups["hphoto"].Value.Trim(),
BlogUrl = item.Groups["hurl"].Value.Trim(),
Url = item.Groups["burl"].Value.Trim(),
});
}
}
catch (Exception ex)
{
}
return blogs;
}
}
代碼分析:
1. Task<IEnumerable<MoBlog>>[] tasks = new Task<IEnumerable<MoBlog>>[nTask]作為并行任務的容器;
2. Task.Factory.StartNew建立對應的任務
3. Task.WaitAll(tasks, TimeSpan.FromSeconds(30));等待容器裡面任務完成30秒後逾時
4. 最後通過把item.Result任務結果添加到集合中,傳回我們需要的資料
這裡解析部落格内容資訊用的正規表達式,這種方式在抓取一定内容上很友善;群裡面有些朋友對正則有點反感,剛接觸的時候覺得挺不好寫的,是以一般都采用更複雜或者其他的解析方式來擷取想要的内容,這裡提出來主要是和這些朋友分享下正則擷取資料是多麼友善,很有必要學習下并且掌握正常的用法,這也是一種苦盡甘來的體驗吧哈哈;
好了咋們建立一個webapi項目取名為 Stage.Api ,使用她自動生成的 ValuesController 檔案裡面的Get方法接口來調用咋們上面實作的部落格抓取方法,代碼如下:
1 // GET api/values
2 public async Task<IEnumerable<MoBlog>> Get(int task = 6)
3 {
4 task = task <= 0 ? 6 : task;
5 task = task > 50 ? 50 : task;
6
7 IBlogsReposity _reposity = new BoKeYuan();
8 return await _reposity.GetBlogs(task);
9 }
這裡使用 IBlogsReposity _reposity = new BoKeYuan(); 來建立和調用具體的實作類,這裡貼出一個線上抓取部落格首頁資訊的位址(不要告訴dudu):
http://www.lovexins.com:1001/api/values?task=6;咋們來想象一下,如果這個Get方法中還需要調用其他實作了接口 IBlogsReposity 的部落格抓取類,那咋們又需要手動new一次來建立對應的對象;倘若除了在 ValuesController.cs 檔案中調用了部落格資料抓取,其他檔案還需要這抓取資料的業務,那麼又會不停的new,可能有朋友就會說那弄一個工廠模式怎麼樣,不錯這是可行的一種方式,不過這裡還有其他方法能處理這種問題,比如:ioc依賴注入;是以就有了下面的分享内容。
首先,我們要使用ninject需要使用nuget下載下傳安裝包,這裡要注意的是Ninject版本比較多,需要選擇合适自己webapi的版本,我這裡選擇的是:
看起來很老了哈哈,不過咋們能用就行,安裝起來可能需要點時間,畢竟比較大麼也有可能是網絡的問題吧;安裝完後咋們建立一個自定義類 NinjectResolverScope 并實作接口 IDependencyScope , IDependencyScope 對應的類庫是 System.Web.Http.dll (注:由于webapi2項目自動生成時候可能勾選了mvc,mvc架構裡面也包含了一個IDependencyScope,是以大家需要注意區分下),好了咋們來直接看下 NinjectResolverScope 實作代碼:
1 /// <summary>
2 /// 解析
3 /// </summary>
4 public class NinjectResolverScope : IDependencyScope
5 {
6
7 private IResolutionRoot root;
8
9 public NinjectResolverScope() { }
10
11 public NinjectResolverScope(IResolutionRoot root)
12 {
13 this.root = root;
14 }
15
16 public object GetService(Type serviceType)
17 {
18 try
19 {
20 return root.TryGet(serviceType);
21 }
22 catch (Exception ex)
23 {
24
25 return null;
26 }
27
28 }
29
30 public IEnumerable<object> GetServices(Type serviceType)
31 {
32 try
33 {
34 return this.root.GetAll(serviceType);
35 }
36 catch (Exception ex)
37 {
38 return new List<object>();
39 }
40 }
41
42 public void Dispose()
43 {
44 var disposable = this.root as IDisposable;
45 if (disposable != null)
46 disposable.Dispose();
47
48 this.root = null;
49 }
50 }
這裡要注意的是GetService和GetServices方法必須使用 try...catch() 包住,經過多方調試和測試,這裡面會執行除手動bind綁定外的依賴,還會執行幾個其他非手動綁定的執行個體對象,這裡使用try避免抛異常影響到程式(其實咋們可以在這裡用代碼過濾掉非手動綁定的幾個執行個體);這裡也簡單說下這個 NinjectResolverScope 中方法執行的先後順序:GetService=》GetServices=》Dispose,GetService主要用來擷取依賴注入對象的執行個體;好了到這裡咋們還需要一個自定義容器類 NinjectResolverContainer ,該類繼承自上面的 NinjectResolverScope 和實作 IDependencyResolver 接口(其實細心的朋友能發現這個 IDependencyResolver 同樣也繼承了 IDependencyScope ),具體代碼如下:
1 public class NinjectResolverContainer : NinjectResolverScope, IDependencyResolver
2 {
3
4 private IKernel kernel;
5
6 public static NinjectResolverContainer Current
7 {
8 get
9 {
10
11 var container = new NinjectResolverContainer();
12
13 //初始化
14 container.Initing();
15 //綁定
16 container.Binding();
17
18 return container;
19 }
20 }
21
22 /// <summary>
23 /// 初始化kernel
24 /// </summary>
25 void Initing()
26 {
27
28 kernel = new StandardKernel();
29 }
30
31 /// <summary>
32 /// 綁定
33 /// </summary>
34 void Binding()
35 {
36
37 kernel.Bind<IBlogsReposity>().To<BoKeYuan>();
38 }
39
40 /// <summary>
41 /// 開始執行
42 /// </summary>
43 /// <returns></returns>
44 public IDependencyScope BeginScope()
45 {
46
47 return new NinjectResolverScope(this.kernel.BeginBlock());
48 }
49 }
這裡能夠看到 IKernel kernel = new StandardKernel(); 這代碼,她們引用都來源于我們安裝的Ninject包,通過調用初始化Initing()後,我們需要在Binding()方法中手動綁定我們對應需要依賴注入的執行個體,Ninject綁定方式有很多種這裡我用的格式是: kernel.Bind<接口>().To<實作類>(); 如此簡單就實作了依賴注入,每次我們需要添加不同的依賴項的時候隻需要在這個Binding()中使用Bind<接口>.To<接口實作類>()即可綁定成功;好了為了驗證咋們測試成功性,我們需要在apiController中使用這個依賴關系,這裡我使用構造函數依賴注入的方式:
1 private readonly IBlogsReposity _reposity;
2
3 public ValuesController(IBlogsReposity reposity)
4 {
5 _reposity = reposity;
6 }
7
8 // GET api/values
9 public async Task<IEnumerable<MoBlog>> Get(int task = 6)
10 {
11 task = task <= 0 ? 6 : task;
12 task = task > 50 ? 50 : task;
13 return await _reposity.GetBlogs(task);
14 }
代碼如上所示,我們運作下程式看下效果:
這個時候提示了個錯誤“沒有預設構造函數”;我們剛才使用的構造函數是帶有參數的,而自定義繼承的 ApiController 中有一個無參數的構造函數,根據錯誤提示内容完全無解;不用擔心,解決這個問題隻需要在 WebApiConfig.cs 中Register方法中增加如下代碼:
1 //Ninject ioc
2 config.DependencyResolver = NinjectResolverContainer.Current;
這句代碼意思就是讓程式執行上面咋們建立的容器 NinjectResolverContainer ,這樣才能執行到我能剛才寫的ioc程式,才能實作依賴注入;值得注意的是 config.DependencyResolver 是webapi自帶的提供的,mvc項目也有同樣提供了 DependencyResolver 給我們使用友善做依賴解析;好了這次我們在運作項目可以得到如圖效果:
首先,安裝Unity和Unity.WebAPI的nuget包,我這裡的版本是:
我們再同樣建立個自定義容器類 UnityResolverContainer ,實作接口 IDependencyResolver (這裡和上面Ninject一樣);然後這裡貼上具體使用Unity實作的方法:
1 public class UnityResolverContainer : IDependencyResolver
2 {
3 private IUnityContainer _container;
4
5 public UnityResolverContainer(IUnityContainer container)
6 {
7 this._container = container;
8 }
9
10 public IDependencyScope BeginScope()
11 {
12 var scopeContainer = this._container.CreateChildContainer();
13 return new UnityResolverContainer(scopeContainer);
14 }
15
16 /// <summary>
17 /// 擷取對應類型的執行個體,注意try...catch...不能夠少
18 /// </summary>
19 /// <param name="serviceType"></param>
20 /// <returns></returns>
21 public object GetService(Type serviceType)
22 {
23 try
24 {
25 //if (!this._container.IsRegistered(serviceType)) { return null; }
26 return this._container.Resolve(serviceType);
27 }
28 catch
29 {
30 return null;
31 }
32 }
33
34 public IEnumerable<object> GetServices(Type serviceType)
35 {
36 try
37 {
38 return this._container.ResolveAll(serviceType);
39 }
40 catch
41 {
42 return new List<object>();
43 }
44 }
45
46 public void Dispose()
47 {
48 if (_container != null)
49 {
50 this._container.Dispose();
51 this._container = null;
52 }
53 }
54 }
這裡和使用Ninject的方式很類似,需要注意的是我們在安裝Unity包的時候會自動在 WebApiConfig.cs 增加如下代碼:
1 //Unity ioc
2 UnityConfig.RegisterComponents();
然後同時在 App_Start 檔案夾中增加 UnityConfig.cs 檔案,我們打開此檔案能看到一些自動生成的代碼,這裡我們就可以注冊綁定我們的依賴,代碼如:
1 public static class UnityConfig
2 {
3 public static void RegisterComponents()
4 {
5 var container = new UnityContainer();
6 container.RegisterType<IBlogsReposity, BoKeYuan>();
7
8 // var lifeTimeOption = new ContainerControlledLifetimeManager();
9 //container.RegisterInstance<IBlogsReposity>(new BoKeYuan(), lifeTimeOption);
10
11 GlobalConfiguration.Configuration.DependencyResolver = new UnityResolverContainer(container);
12 }
13 }
這裡展示了兩種注冊依賴的方式: container.RegisterType<IBlogsReposity, BoKeYuan>(); 和 container.RegisterInstance<IBlogsReposity>(new BoKeYuan(), lifeTimeOption); ,當然還有其他的擴充方法這裡就不舉例了;最後一句代碼: GlobalConfiguration.Configuration.DependencyResolver = new UnityResolverContainer(container); 和我們之前Ninject代碼一樣,隻是換了一個地方和執行個體化寫法方式而已,各位可以仔細對比下;其實 UnityConfig.cs 裡面的内容都可以移到 WebApiConfig.cs 中去,unity自動分開應該是考慮到代碼内容分塊來管理吧,好了同樣我們使用自定義的 ValuesController 的構造函數來添加依賴:
1 public class ValuesController : ApiController
2 {
3
4 private readonly IBlogsReposity _reposity;
5
6 public ValuesController(IBlogsReposity reposity)
7 {
8 _reposity = reposity;
9 }
10
11 // GET api/values
12 public async Task<IEnumerable<MoBlog>> Get(int task = 6)
13 {
14 task = task <= 0 ? 6 : task;
15 task = task > 50 ? 50 : task;
16 return await _reposity.GetBlogs(task);
17 }
18 }
從代碼上來看,這裡面Ninject和Unity的注入方式沒有差異,這樣能就讓我們開發程式的時候兩種注入方式可以随便切換了,最後來我這裡提供一個使用這個webapi擷取資料綁定到頁面上的效果:
外網浏覽位址:
http://www.lovexins.com:1001/home,本章的分享内容就到這裡,希望能給大家帶來幫助,也希望大家不吝點“推薦”,謝謝。
git位址:
https://github.com/shenniubuxing3nuget釋出包:
https://www.nuget.org/profiles/shenniubuxing3