在寫MVC網站時
如何讓物件在每次的請求都不一樣呢?(例如:Entity Framework的DbEntities)
本篇會使用Unity所提供的方法
首先你可能會問 : 直接套Singleton Pattern不就好了?
是的,一開始我也是這麼想的
不過測試之後發現不行(不然就不會發文了orz)
為什麼不行呢
目前大部分MVC網站都是架在IIS上時(dotnet core除外)
IIS在收到Request一段時間後執行緒才會自殺(有講錯請指教)
所以實際上你在這段時間內用Singleton拿到的物件會是同一個
如果你使用Singleton Pattern或Unity所提供的RegisterSingleton去註冊物件時
然後連續發送兩次請求
你會發現第一次跟第二次拿到的其實是同一個物件
關於Unity
我也只是入門而已
可以參考Kevin大的文章
或者請益G神
那麼在實作一開始
請先建立一個空白的MVC專案
並加入一個DateTimeRecorder類別
我們會利用觀察這個物件被建立的時間
來判斷是否有達到PerRequestSingleton的效果
DateTimeRecorder.cs
public class DateTimeRecorder
{
private DateTime date;
public DateTimeRecorder()
{
date = DateTime.Now;
}
public string GetRecordTime() => date.ToLongTimeString();
}
接著使用Nuget安裝Unity.Mvc5
它會順便幫你把Unity一起安裝
[實作一 - 使用Unity內建的Singleton做測試]
裝好之後
在專案目錄App_Start資料夾底下
它會自動幫你加入一個UnityConfig.cs
接著將DateTimeRecorder註冊為Singleton
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterSingleton<DateTimeRecorder>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
好了之後記得修改Global.asax的Application_Start
將UnityConfig註冊上去
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
UnityConfig.RegisterComponents();
}
然後請在HomeController.cs宣告一個建構式注入
接著把時間回傳到Index上
public class HomeController : Controller
{
private readonly DateTimeRecorder _recorder;
public HomeController(DateTimeRecorder recorder)
{
this._recorder = recorder;
}
public ActionResult Index()
{
var recordTime = _recorder.GetRecordTime();
return View(recordTime as object);
}
}
這裡的recordTime還要轉成object
是因為它會預設吃到其他簽章
記得要改一下Index.cshtml
@model string
@{
ViewBag.Title = "Home Page";
}
<h2>DateTimeRecorder建立時間: @Model </h2>
<h2>現在時間: @(DateTime.Now.ToLongTimeString())</h2>
好的,接著開始第一次Request
按下F5發送第二次Request
比較了一下兩次DateTimeRecorder被建立的時間
發現是一樣的結果(在IIS執行緒自殺之前應該都是這個時間)
由此可得知使用Singleton並不能達到我們要的PerRequestSingleton
[實作二 - 使用Unity.Mvc內建的PerRequestLifetimeManager做測試]
其實Unity內建有提供對應的LifetimeManager
但如果你跟我一樣一開始就裝Unity.Mvc5(想說比較新嘛)
那就會很悲劇的找不到
因為這個方法只活在Unity.Mvc裡(不知道為什麼Unity.Mvc5沒有)
所以請記得使用Nuget再次安裝Unity.Mvc
好了之後一樣修改一下UnityConfig.cs
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
//container.RegisterSingleton<DateTimeRecorder>();
container.RegisterType<DateTimeRecorder>(new PerRequestLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
好了之後一樣執行測試
接著一樣重新整理
可以發現DateTimeRecorder的建立時間會隨著每次Request有所不同
這樣一來就達到PerRequestSingleton的效果囉!
下篇再補充如何在不使用Unity的情況達到一樣的效果