webapi - 使用依賴注入

神牛003發表於2017-01-05

本篇將要和大家分享的是webapi中如何使用依賴注入,依賴注入這個東西在介面中常用,實際工作中也用的比較頻繁,因此這裡分享兩種在api中依賴注入的方式NinjectUnity;由於快過年這段時間打算了解下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,本章的分享內容就到這裡,希望能給大家帶來幫助,也希望大家不吝點“推薦”,謝謝。

相關文章