本篇將要和大家分享的是webapi中如何使用依賴注入,依賴注入這個東西在接口中常用,實際工作中也用的比較頻繁,因此這裡分享兩種在api中依賴注入的方式Ninject和Unity;由於快過年這段時間打算了解下vue.js,所以後面對webapi的分享文章可能會慢點更新,希望支持的朋友們多多諒解,畢竟只有不斷充電學習,才能更好的適應it行業吧;本章內容希望大家喜歡,也希望各位多多掃碼支持和推薦謝謝:
» Task並行任務抓取博客園首頁信息
» IOC框架Ninject的使用
» IOC框架Unity的使用
下面一步一個腳印的來分享:
» Task並行任務抓取博客園首頁信息
首先,咋們需要創建一個博客信息實體類 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依賴注入;因此就有了下面的分享內容。
» IOC框架Ninject的使用
首先,我們要使用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 給我們使用方便做依賴解析;好了這次我們在運行項目可以得到如圖效果:
» IOC框架Unity的使用
首先,安裝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,本章的分享內容就到這裡,希望能給大家帶來幫助,也希望大家不吝點“推薦”,謝謝。