Ninject之旅之三:Ninject對象生命周期


摘要

DI容器的一個責任是管理他創建的對象的生命周期。他應該決定什么時候創建一個給定類型的對象,什么時候使用已經存在的對象。他還需要在對象不需要的時候處理對象。Ninject在不同的情況下管理對象的生命周期提供了強大的支持。在我們定義一個綁定的時候,定義創建對象的范圍。在那個范圍內,對象將被重用,每次綁定只存在一次。注意,對象不允許依賴於生命周期短自己小的對象。

1、暫時范圍

在暫時態范圍內,對象生命周期不被Ninject進行管理。任何時候請求一個類型的對象,都將創建一新對象。Ninject不管理保持創建的對象或者在范圍內處理他。這是Ninject默認的范圍。如果不指定范圍,默認是暫時態。在上一篇文章里,ConsoleLogger和MailServer對象都是暫時態,因為沒有指定他的范圍。

2、單例范圍

有時候我們不想每次需要的時候都創建一個新的對象,這時候使用單例。有兩種方法創建單例。一種是使用單例模式。一種是使用Ninject方法InSingletonScope。
1)使用單例模式:

 1 class ConsoleLogger:ILogger
 2 {
 3     public static readonly ConsoleLogger Instance = new ConsoleLogger();
 4     private static ConsoleLogger()
 5     {
 6         // Hiding constructor
 7     }
 8     public void Log(string message)
 9     {
10         Console.WriteLine("{0}: {1}", DateTime.Now, message);
11     }
12 }

然后在Bind方法后調用ToConstant方法指定靜態只讀對象ConsoleLogger.Instance為常量對象。

kernel.Bind<ILogger>().ToConstant(ConsoleLogger.Instance);

2)使用方法InSingletonScope:

kernel.Bind<ILogger>().To<ConsoleLogger>().InSingletonScope();

如果要給MailServerConfig類對象設置單例,則先調用ToSelf方法將他綁定自身,然后再調用方法InSingletonScope。

kernel.Bind<MailServerConfig>().ToSelf().InSingletonScope();

3、線程范圍

如果定義在線程范圍內,每一個線程將只創建一個給定類型的對象。對象的生命周期跟對象所在的線程一樣長。

調用方法InThreadScope創建線程范圍:

kernel.Bind<object>().ToSelf().InThreadScope();

創建兩個Test方法測試線程范圍。

 1 using Ninject;
 2 using NUnit.Framework;
 3 using System.Threading;
 4 
 5 namespace Demo.Ninject
 6 {
 7     [TestFixture]
 8     class NinjectTest
 9     {
10         [Test]
11         public void ReturnsTheSameInstancesInOneThread()
12         {
13             using (var kernel = new StandardKernel())
14             {
15                 kernel.Bind<object>().ToSelf().InThreadScope();
16                 var instance1 = kernel.Get<object>();
17                 var instance2 = kernel.Get<object>();
18                 Assert.AreEqual(instance1, instance2);
19             }
20         }
21 
22         [Test]
23         public void ReturnsDifferentInstancesInDifferentThreads()
24         {
25             var kernel = new StandardKernel();
26             kernel.Bind<object>().ToSelf().InThreadScope();
27             var instance1 = kernel.Get<object>();
28             new Thread(() =>
29             {
30                 var instance2 = kernel.Get<object>();
31                 Assert.AreNotEqual(instance1, instance2);
32                 kernel.Dispose();
33             }).Start();
34         }
35     }
36 }

第一個方法在同一個線程內請求了兩個object對象,他們是相同的實例。第二個方法先在主線程內請求一個object實例,然后開啟另一個線程請求另一個實例,他們不是相同的實例。

需要添加NUnit和NUnit.Console才能測試上面的方法。我使用的是NUnit 2.6.4和NUnit.Console 2.0.0。

4、請求范圍

請求范圍在web應用程序里非常有用。可以在相同的請求范圍內得到一個單例的對象。一旦一個請求被處理,另一個請求到來,Ninject創建新的對象實例,並保持他直到請求結束。

調用方法InRequestScope設置請求范圍,例如:

kernel.Bind<MailServerConfig>().ToSelf().InRequestScope();

需要添加Ninject.Web.Common引用才能夠調用InRequestScope方法。

5、自定義范圍

自定義范圍讓我們定義我們自己的范圍,在這個范圍內保持一類型的唯一對象。只要提供的回調方法返回的對象引用是一樣的,Ninject在這個范圍內返回相同的實例。只要返回的對象引用變了,將創建一新的指定類型的對象。創建的對象實例將一直保存在緩存里,直到返回的范圍對象被垃圾回收器回收。一旦范圍對象被垃圾回收器回收,Ninject創建的所有的對象實例將被從緩存中釋放和處理。

調用InScope方法傳入Lamda表達式定義自定義返回。例如:

kernel.Bind<object>().ToSelf().InScope(ctx => User.Current);

用例子來介紹自定義范圍:

1)創建User類。

1     class User
2     {
3         public string Name { get; set; }
4         public static User Current { get; set; }
5     }

2)創建函數ReturnsTheSameInstancesForAUser。

 1         [Test]
 2         public void ReturnsTheSameInstancesForAUser()
 3         {
 4             using (var kernel = new StandardKernel())
 5             {
 6                 kernel.Bind<object>().ToSelf().InScope(ctx => User.Current);
 7                 User.Current = new User();
 8                 var instance1 = kernel.Get<object>();
 9                 User.Current.Name = "Foo";
10                 var instance2 = kernel.Get<object>();
11                 Assert.AreEqual(instance1, instance2);
12             }
13         }

雖然User.Current.Name的值變了,但是User.Current的引用沒變。因此,兩次請求返回的對象是同一個對象。

3)創建函數ReturnsDifferentInstancesForDifferentUsers。

 1         [Test]
 2         public void ReturnsDifferentInstancesForDifferentUsers()
 3         {
 4             using (var kernel = new StandardKernel())
 5             {
 6                 kernel.Bind<object>().ToSelf().InScope(ctx => User.Current);
 7                 User.Current = new User();
 8                 var instance1 = kernel.Get<object>();
 9                 User.Current = new User();
10                 var instance2 = kernel.Get<object>();
11                 Assert.AreNotEqual(instance1, instance2);
12             }
13         }

因為改變了User.Current對象引用,因此,兩次請求返回的對象是不同的對象。

你可能注意到了回調函數提供了一個名字是ctx的IContext參數。這個對象提供了綁定的用來創建范圍對象的上下文環境。自定義范圍是最靈活最有用的范圍。其實其他范圍都可以用自定義范圍來實現。

線程范圍:

kernel.Bind<object>().ToSelf().InScope(ctx=>Thread.CurrentThread);

請求范圍:

kernel.Bind<object>().ToSelf().InScope(ctx=>HttpContext.Current);

可以使用Release方法強制釋放創建的對象實例。

1 var myObject = kernel.Get<MyService>();
2 ..
3 kernel.Release(myObject);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM