會考慮使用 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 回應:
張貼留言