• ASP.NET Web Forms 的 DI 應用範例


    跟 ASP.NET MVC 与 Web API 比起来,在 Web Forms 应用程式中使用 Dependency Injection 要来的麻烦些。这里用一个范例来说明如何注入相依物件至 Web Forms 的 ASPX 页面。

    使用的开发工具与类别库:

    • Visual Studio 2013
    • .NET Framework 4.5
    • Unity 3.5.x


    问题描述

    基于测试或其他原因,希望 ASPX 网页只依赖特定服务的介面,而不要依赖具象类别。

    假设首页 Default.aspx 需要一个传回“Hello World!”字串的服务,而我们将此服务的介面命名为 IHelloService。以下为此服务的介面与实作类别:

    复制代码
    public interface IHelloService
    {
        string Hello(string name);
    }
     
    public class HelloService : IHelloService
    {
        public string Hello(string name)
        {
            return "Hello, " + name;
        }
    }
    复制代码

    Default.aspx 的 code-behind 类别大概会像这样: 

    复制代码
    public partial class Default : System.Web.UI.Page
    {
        public IHelloService HelloService { get; set; }
     
        protected void Page_Load(object sender, EventArgs e)
        {
            // 在網頁上輸出一段字串訊息。訊息內容由 HelloService 提供。
            Response.Write(this.HelloService.Hello("DI in ASP.NET Web Forms!"));
        }
    }
    复制代码

    问题来了:Page 物件是由 ASP.NET Web Forms 框架所建立的,我们如何从外界动态注入 IHelloService 物件呢?


    解法

    一般而言,我们建议尽量采用 Constructor Injection 来注入相依物件,可是此法很难运用在 Web Forms 的 Page 物件上。一个便宜行事的解法是采用 Mark Seemann 所说的“私生注入”(Bastard Injection),像这样:

    复制代码
    public partial class Default : System.Web.UI.Page
    {
        public IHelloService HelloService { get; set; }
     
        public Default()
        {
            // 透过一个共用的 Container 物件来解析相依物件。
            this.HelloService = AppShared.Container.Resolve<IHelloService>();
        }
     
        protected void Page_Load(object sender, EventArgs e)
        {
            // 在网页上输出一段字串讯息。讯息内容由 HelloService 提供。
            Response.Write(this.HelloService.Hello("DI in ASP.NET Web Forms!"));
        }
    }
    复制代码

    此解法的一个问题是,你必须在每一个 ASPX 页面的 code-behind 类别中引用 DI 容器的命名空间,而这样就变成到处都依赖特定的 DI 容器了。我们希望尽可能把呼叫 DI 容器的程式码集中写在少数几个地方就好。


    接下来的实作步骤会利用一个 HTTP handler 来拦截 Page 物件的建立程序,以便在 Page 物件建立完成后,立刻以 Property Injection 的方式将 Page 物件需要的服务给注入进去。

    實作步驟

    Step 1:建立新专案

    建立一个新的 ASP.NET Web Application 专案,目标平台选择 .NET Framework 4.5,专案名称命名为:WebFormsDemo。

    专案范本选择 Empty,然后在 Add folder and core references for 项目上勾选“Web Forms”。

    专案建立完成后,透过 NuGet 管理员加入 Unity 套件。


    Step 2:注册型别

    在应用程式的“组合根”建立 DI 容器并注册相依型别。这里选择在 Global_asax.cs 的 Application_Start 方法中处理这件事:

    复制代码
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            var container = new UnityContainer();
            Application["Container"] = container; // 把容器物件保存在共用變數裡
     
            // 註冊型別
            container.RegisterType<IHelloService, HelloService>();
        }
    }
    复制代码

    Step 3:撰寫 HTTP Handler


    在专案根目录下建立一个子目录:Infrastructure,然后在此目录中加入一个新类别:UnityPageHandlerFactory.cs。程式码:

    复制代码
    public class UnityPageHandlerFactory : System.Web.UI.PageHandlerFactory
    {
        public override IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path)
        {
            Page page = base.GetHandler(context, requestType, virtualPath, path) as Page;
            if (page != null)
            {
                var container = context.Application["Container"] as IUnityContainer;
                var properties = GetInjectableProperties(page.GetType());
     
                foreach (var prop in properties)
                {
                    try
                    {
                        var service = container.Resolve(prop.PropertyType);
                        if (service != null)
                        {
                            prop.SetValue(page, service);
                        }
                    }
                    catch
                    {
                        // 沒辦法解析型別就算了。
                    }
                }
            }
            return page;
        }
     
        public static PropertyInfo[] GetInjectableProperties(Type type)
        {
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            if (props.Length == 0)
            {
                // 傳入的型別若是由 ASPX 頁面所生成的類別,那就必須取得其父類別(code-behind 類別)的屬性。
                props = type.BaseType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            }
            return props;
        }       
    }
    复制代码

    程式說明:

    • ASP.NET Web Forms 框架会呼叫此 handler 物件的 GetHandler 方法来建立 Page 物件。
    • 在 GetHandler 方法中,先利用父类别来建立 Page 物件,然后紧接着进行 Property Injection 的处理。首先,从 Application["Container"] 中取出上一个步骤所建立的 DI 容器,接着找出目前的 Page 物件有宣告哪些公开属性,然后利用 DI 容器来逐一解析各属性的型别,并将建立的物件指派给属性。
    • 静态方法 GetInjectableProperties 会找出指定型别所宣告的所有公开属性,并传回呼叫端。注意这里只针对“Page 类别本身所宣告的公开属性”来进行 Property Injection,这样就不用花时间在处理由父类别继承而来的数十个公开属性。


    Step 4:註冊 HTTP Handler

    在 web.config 中註冊刚才写好的 HTTP handler:

    复制代码
    <configuration>
      <system.web>
        <compilation debug="true" targetFramework="4.5" />
        <httpRuntime targetFramework="4.5" />
      </system.web>
     
      <system.webServer>
        <handlers>
          <add name="UnityPageHandlerFactory" path="*.aspx" verb="*" type="WebFormsDemo.Infrastructure.UnityPageHandlerFactory"/>
        </handlers>
      </system.webServer>
    </configuration>
    复制代码

    基础建设的部分到此步骤已经完成,接着就是撰写各个 ASPX 页面。

    Step 5:撰写测试页面

    在专案中加入一个新的 Web Form,命名为 Default.aspx。然后在 code-behind 类别中宣告相依服务的属性,并且在其他地方呼叫该服务的方法。参考以下范例:

    复制代码
    public partial class Default : System.Web.UI.Page
    {
        public IHelloService HelloService { get; set; }
     
        protected void Page_Load(object sender, EventArgs e)
        {
            // 在網頁上輸出一段字串訊息。訊息內容由 HelloService 提供。
            Response.Write(this.HelloService.Hello("DI in ASP.NET Web Forms!"));
        }
    }
    复制代码

    你可以看到,ASPX 网页并不需要引用 Unity 容器的命名空间,因为注入相依物件的动作已经由基础建设预先帮你处理好了。


    Step 6:執行看看

    执行时,浏览器应该会显示一行讯息:「Hello, DI in ASP.NET Web Forms!」

  • 相关阅读:
    OGRE 3D 1.7 Beginner‘s Guide中文版 第一章
    一个人的成功取决于晚上的8点至10点--经典语录必读
    学历代表过去、能力代表现在、学习力代表未来
    理财达人五步走
    Ogre场景、节点、摄像机通过自动、鼠标、键盘控制移动
    QT按钮背景颜色设置及文字显示位置设置
    Qt一个工程调用另一个工程的类成员变量
    C++搜索字符串中的汉字
    Q窗口操作函数(窗口最大化,全屏,隐藏最大化最小化按钮)
    PAT(Advance Level)Practice1001
  • 原文地址:https://www.cnblogs.com/lyl6796910/p/3938210.html
Copyright © 2020-2023  润新知