會考慮使用 Properties Autowired 是經過考慮跟評估的,畢竟 Properties Autowired 會造成負面的影響,就是 class 所相依的 class 會不清楚,很難像 Constructor 那樣清楚明白,但一般正常不會自己去建構 Controller 跟 PageModel,要建構的成本太大了,所以 Controller 跟 PageModel 很適合用 Properties Autowired。
為了做到 Properties Autowired 可以用 IPageFilter 進行處理:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
namespace XXXXX.Api.Filters
{
public class PageModelInjectFilter : IPageFilter
{
/// <summary>注入器的快取</summary>
private readonly ConcurrentDictionary<Type, Action<PageModel, IServiceProvider>> _injecterCache =
new ConcurrentDictionary<Type, Action<PageModel, IServiceProvider>>();
/// <summary>建構注入器</summary>
private Action<PageModel, IServiceProvider> buildInjecter(Type type)
{
/* delegate 具有疊加的能力,先建構一個空的 delegate */
Action<PageModel, IServiceProvider> action = (page, provider) => { };
/* 取得 Properties 且是可以寫入,並具有 [Inject] Attribute */
var props = type.GetProperties()
.Where(p => p.CanWrite)
.Where(p => p.IsDefined(typeof(InjectAttribute)));
foreach (var prop in props)
{
action += (page, provider) =>
{
/* 如果 Property 是已經有 Value 的就不要進行注入 */
if (prop.GetValue(page) != null) { return; }
/* 從 provider 取得依賴的物件 */
object value = provider.GetRequiredService(prop.PropertyType);
prop.SetValue(page, value);
};
}
return action;
}
/// <summary>在選取處理常式方法之後,但在進行模型系結之前呼叫。</summary>
public void OnPageHandlerSelected(PageHandlerSelectedContext context)
{
/* 判斷是否是 PageModel */
var page = context.HandlerInstance as PageModel;
if (page == null) { return; }
/* 取得或建構注入器 */
Action<PageModel, IServiceProvider> injecter =
_injecterCache.GetOrAdd(page.GetType(), buildInjecter);
/* 進行注入 */
injecter(page, context.HttpContext.RequestServices);
}
/// <summary>在完成模型系結之後,于處理常式方法執行之前呼叫。</summary>
public void OnPageHandlerExecuting(PageHandlerExecutingContext context) { }
/// <summary>在處理常式方法執行之後,在動作結果執行之前呼叫。</summary>
public void OnPageHandlerExecuted(PageHandlerExecutedContext context) { }
}
}
接著在 Startup.cs 進行過濾器配置
public void ConfigureServices(IServiceCollection services)
{
//...
IMvcBuilder mvcBuilder = services
.AddRazorPages(options =>
{
//...
})
.AddMvcOptions(options =>
{
//...
options.Filters.Add(new PageModelInjectFilter());
})
;
//...
}
然後在 PageModel 就可以用下面的方式撰寫:
public class IndexModel : PageModel
{
[Inject] public IMenuProvider MenuProvider { private get; set; }
public IActionResult OnGet()
{
return Page();
}
}
0 回應:
張貼留言