無論是什麼架構,當要產生MVC 的Controller時,需要ControllerFactory。在 Dot Net Core中,Host於第一次執行EndpointMiddleware時,會產生ControllerFactory。回顧 ”Http Request to Dot Net Core MVC”中的圖:
當S4的步驟,要執行ControllerFactory,此時被呼叫的委派是在Host於第一次執行EndpointMiddleware時產生。而產生的方式是用封閉委派的技巧。
接下來會展示此技巧如何將記憶體保留起來;其實很像javascript的 closure。
首先我們先準備好類別:
public class Provider
{
private string Info;
private DateTime Date;
public Func<string, string> CreateFactory(Descriptor descriptor)
{
this.Info = descriptor.Info;
this.Date = descriptor.Date;
string CreateMessage(string WantToSay)
{
return $" I want to say is {WantToSay} . More Info is {Info}, on {this.Date.ToString("yyyy-MM-dd")} ";
}
return CreateMessage;
}
}
Provider 類別中,我們會有 “Info” 與 “Date” 二個欄位,與CreateFactory 函式,將會回傳一個委派函示。然後再準備一個將資料包裝起來的類別” Descriptor”:
public class Descriptor
{
public string Info;
public DateTime Date;
public Descriptor(string strInfo, DateTime date)
{
this.Info = strInfo;
this.Date = date;
}
}
接下來在 Startup. Configure 函式中做初始化資料(comment: Demo example: initial Data):
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
//Demo example: initial Data
Descriptor dc = new Descriptor("Generating Description was in Configure Method of Startup Class !", DateTime.Now);
Provider provider = new Provider();
CommonDelegate.DelegateFunc = provider.CreateFactory(dc);
}
}
從上可以看到在Startup. Configure 函式中,會新增一個Descriptor 的instance,然後利用Provider. CreateFactory將委派函式接收Descriptor的資料後回傳到公用的委派變數。一般來說,離開Startup. Configure後Descriptor 的instance的記憶體會被回收掉,不會存在;而這邊使用一個封閉委派的技巧將Descriptor 的instance的記憶體保留在委派的函式內。
然後到Controller Action的地方去執行此委派函式(comment:Demo example):
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
//Demo example
System.Diagnostics.Debug.WriteLine("Example1:");
System.Diagnostics.Debug.WriteLine( CommonDelegate.DelegateFunc("Hello Example1 !") );
System.Diagnostics.Debug.WriteLine("\n");
return View();
}
}
會發現執行到此Action時,透過委派函式仍然可以取得到之前在Startup. Configure內所宣告的區域instance “Descriptor”。
實際上執行畫面如下:
會將” Generating Description was in Configure Method of Startup Class !”字串與日期保存到Descriptor 的instance中,接著離開Startup. Configure去執行Controller Action Method:
會發現委派函式仍然可以取得Descriptor 的instance中的字串與日期。
這和Javascript 的 Closure 的記憶體保留狀況非常類似。
同樣的效果也可以使用Delegate.CreateDelegate 來達成。
首先準備好類別:
public class Package
{
private int PackID;
public Package(int id)
{
this.PackID = id;
}
public void Method1(string Input)
{
System.Diagnostics.Debug.WriteLine
(" Using Delegate.CreateDelegate And Calling Method1 : PackID = {0}, Input = {1} ", this.PackID, Input);
}
}
public static class PackageDelegate
{
public delegate void PackageDelegateFunc(string Input);
public static Delegate DelegateFunc;
}
然後於Startup. Configure做初始化委派:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
//E
Package pk = new Package(DateTime.Now.Millisecond);
MethodInfo method1 = typeof(Package).GetMethod("Method1", BindingFlags.Public | BindingFlags.Instance);
PackageDelegate.DelegateFunc = (PackageDelegate.PackageDelegateFunc)Delegate.CreateDelegate(typeof(PackageDelegate.PackageDelegateFunc), pk, method1);
}
}
接著於Controller Action中執行:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
//Example2
System.Diagnostics.Debug.WriteLine("Example2:");
PackageDelegate.PackageDelegateFunc pkFunc = (PackageDelegate.PackageDelegateFunc)PackageDelegate.DelegateFunc;
pkFunc("Hello Example2 !");
System.Diagnostics.Debug.WriteLine("\n");
return View();
}
}
執行後,當初在Startup. Configure所初始化的內容也可以在委派函式中取得!