개요

ASP.NET WebForm 에서 Dependency Injection 을 사용하는 방법을 소개한다. IoC Container 를 이용하여 System.Web.UI.Page 를 상속하는 페이지에서 Injection 을 해야 하는데, 이를 위해 IHttpHandlerFactory 를 사용하는 방법을 소개한다.

여기에서는 필자가 꾸준히 만들어 온 Unity ContainerWindsor Castle 을 기반으로 하는 Umc.Core 프레임워크를 사용한다.Umc.Core 프레임워크에는 Unity Auto Registration 기능등이 모두 포함하기 때문에 프로젝트 셋업에 편리하다는 장점도 있다.

샘플 프로젝트는 필자의 github 에서 다운로드 받을 수 있다.
https://github.com/powerumc/WebForm-DependencyInjection 

프로젝트 셋업

먼저 프로젝트를 만들고, nuget 을 이용하여 umc.core 프레임워크를 설치한다.

nuget install umc.core

그리고, Umc.Core 에 구현에 놓은 IHttpHandlerFactory 를 샘플 프로젝트에 추가해 놓았다. 이를 web.config 에 추가해 주면 된다.

<system.webServer>
    <handlers>
      <add name="WebFormPageHandlerFactory" verb="*" path="*.aspx"  type="WebForm_DependencyInjection.FrameworkContainerPageHandlerFactory"/>
    </handlers>
</system.webServer>

FrameworkContainerPageHandlerFactory 클래스의 구현은 아래의 코드를 참고하면 된다. 다만, 아래 구현 코드에서 BuildManager.CreateInstanceFromVirtualPath 를 대신 사용하면 절대 안된다.

public class FrameworkContainerPageHandlerFactory : IHttpHandlerFactory
{
    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
        var handler = BuildManager.GetObjectFactory(url, false).CreateInstance();
        if (handler.GetType().ToString().StartsWith("ASP."))
        {
            var container = context.Application["container"] as IFrameworkContainer;
            return container.Resolve(handler.GetType().BaseType) as IHttpHandler;
        }

        return handler as IHttpHandler;
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
    }
}

웹 응용프로그램 Bootstrap

그 다음 할 일은 어떤 컴포넌트들을 IoC 에 등록하고 이를 Composition 할 지 코드를 통해 구현한다. ASP.NET WebForm 에서는 Global.asax.cs 의 Application_Start 메서드에 구현하는 게 가장 적절하다.

그러나 이는 RELEASE 용 빌드인 경우가 그렇고, 개발 중인 경우, 즉 DEBUG 모드 빌드인 경우 Session_Start 에 구현해 놓는 것도 좋을 것 같다.

public class Global : System.Web.HttpApplication
{
  private static IFrameworkContainer container;

  protected void Application_Start(object sender, EventArgs e)
  {
      container = new FrameworkContainerForUnity();

      var catalog = new FrameworkAssemblyCatalog(Assembly.GetExecutingAssembly());
      var visitor = new FrameworkDependencyVisitor(catalog);
      var resolver = new FrameworkCompositionResolverForUnity((FrameworkContainerForUnity)container, visitor.VisitTypes());
      resolver.Compose();

      Application.Lock();
      Application["container"] = container;
      Application.UnLock();
    }
  }

FrameworkAssemblyCatalogFrameworkCatalog를 구현한 클래스로 컴포넌트를 등록하는 방법을 구현하는 클래스다.

FrameworkDependencyVisitor 는 catalog 에서 검색된 컴포넌트들을 구석 구석 방문해서 Comsoition 을 위해 객체 그래프를 그리는 클래스다.

FrameworkCompositionResolverForUnity 는 객체 그래프를 IoC Container 에 등록하는 클래스다.

이렇게 몇 줄의 코드로 Auto Registration 과정이 모두 끝난다.

서비스 구현

간단하게 Dependency Inection 을 테스트하기 위해서 IEmailService 인터페이스와 이를 구현한 코드다.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using Umc.Core;

namespace WebForm_DependencyInjection.Services
{
    public interface IEmailService
    {
        bool Send(string to, string contents);
    }

    [DependencyContract(typeof(IEmailService))]
    public class EmailService : IEmailService
    {
        public bool Send(string to, string contents)
        {
            HttpContext.Current.Response.Write("Send email.");
            return true;
        }
    }
}

페이지 구현

이제 모두 다 됐다. Dependency Injection 이 필요한 프로퍼티에 [DependencyInjection] 특성을 선언하면 Index 페이지 인스턴스가 생성될 때 컨테이너에 등록된 컴포넌트가 주입된다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Umc.Core;
using WebForm_DependencyInjection.Services;

namespace WebForm_DependencyInjection
{
    public partial class Index : System.Web.UI.Page
    {
        // Here.. Injection for IEmailService.
        [DependencyInjection]
        protected IEmailService EmailService { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            EmailService.Send("", "");
        }
    }
}


Posted by 땡초 POWERUMC

댓글을 달아 주세요

Flip-Table-Net 은 자바 코드로 작성된 flip-table 을.NET 코드로 포팅한 프로젝트로, 콘솔에 데이터를 표로 표현해 줍니다.

설치

Command Line 에서 다음처럼 입력하거나,

nuget install flip-tables-net

Visual Studio Package Manager Console 에서 다음처럼 입력합니다..

Install-Package Flip-Tables-Net

또는 Nuget 패키지 관리자에서 flip-table-net 으로 검색합니다.

기존 자바에서 지원하던 기능

FlipTable은 헤더 정보와 데이터 정보가 필요합니다.

string[] headers = { "Test", "Header" };
string[][] data =
{
    new[] {"Foo", "Bar"},
    new[] {"Kit", "Kat"}
};
Console.WriteLine(FlipTable.Of(headers, data));
+======+========+
| Test | Header |
+======+========+
| Foo  | Bar    |
+------+--------+
| Kit  | Kat    |
+======+========+

데이터에 개행 문자열도 지원합니다.

string[] headers = { "One Two\nThree", "Four" };
string[][] data = { new[] { "Five", "Six\nSeven Eight" } };
Console.WriteLine(FlipTable.Of(headers, data));
+=========+=============+
| One Two | Four        |
| Three   |             |
+=========+=============+
| Five    | Six         |
|         | Seven Eight |
+=========+=============+

그리고 테이블 안의 테이블도 지원합니다.

string[] innerHeaders = { "One", "Two" };
string[][] innerData = { new[] { "1", "2" } };
string inner = FlipTable.Of(innerHeaders, innerData);
string[] headers = { "Left", "Right" };
string[][] data = { new[] { inner, inner } };
Console.WriteLine(FlipTable.Of(headers, data));
+===============+===============+
| Left          | Right         |
+===============+===============+
| +=====+=====+ | +=====+=====+ |
| | One | Two | | | One | Two | |
| +=====+=====+ | +=====+=====+ |
| | 1   | 2   | | | 1   |   2 | |
| +=====+=====+ | +=====+=====+ |
|               |               |
+===============+===============+

.NET 으로 포팅하면서 추가된 기능

flip-tables-net 버전은 .NET 이 지원하는 객체를 사용할 수 있습니다.

DataTableDataSet 을 사용하는 방법입니다.

var dt = new DataTable();
dt.Columns.Add("FirstName");
dt.Columns.Add("LastName");
dt.Columns.Add("Age");
var row1 = dt.NewRow();
row1["FirstName"] = "Junil";
row1["LastName"] = "Um";
row1["Age"] = 37;
dt.Rows.Add(row1);

Console.WriteLine(dt.FlipTablesFrom());
+===========+==========+=====+
| FirstName | LastName | Age |
+===========+==========+=====+
| Junil     | Um       | 37  |
+===========+==========+=====+

.NET nested entity object 객체는 다음과 같이 정의되어 있다면,

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public List<Person> Children { get; set; }
    public List<Name> Names { get; set; }

    public Person() { }
    public Person(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
        Children = new List<Person>();
        Names = new List<Name>() { new Name("A", "B") };
    }
}

public class Person2
{
    public Name Name { get; set; }
    public int Age { get; set; }
}

public class Name
{
    public string First { get; set; }
    public string Last { get; set; }

    public Name() { }

    public Name(string first, string last)
    {
        First = first;
        Last = last;
    }
}
var person2 = new Person2()
{
    Name = new Name("Junil", "Um"),
    Age = 37
};
Console.WriteLine(person2.FlipTablesFrom());
+==================+=====+
| Name             | Age |
+==================+=====+
| +=======+======+ | 37  |
| | First | Last | |     |
| +=======+======+ |     |
| | Junil |   Um | |     |
| +=======+======+ |     |
|                  |     |
+==================+=====+

복합적인 데이터가 담긴 entity model 와 List<> 객체입니다.

var personList = new List<Person>
{
    new Person("Junil", "Um", 37),
};
personList[0].Children.Add(new Person("A", "B", 12));
Console.WriteLine(personList.FlipTablesFrom());
+===========+==========+=====+==============================================================+==================+
| FirstName | LastName | Age | Children                                                     | Names            |
+===========+==========+=====+==============================================================+==================+
|     Junil |       Um |  37 | +===========+==========+=====+==========+==================+ | +=======+======+ |
|           |          |     | | FirstName | LastName | Age | Children |            Names | | | First | Last | |
|           |          |     | +===========+==========+=====+==========+==================+ | +=======+======+ |
|           |          |     | |         A |        B |  12 |          | +=======+======+ | | |     A |    B | |
|           |          |     | |           |          |     |          | | First | Last | | | +=======+======+ |
|           |          |     | |           |          |     |          | +=======+======+ | |                  |
|           |          |     | |           |          |     |          | |     A |    B | | |                  |
|           |          |     | |           |          |     |          | +=======+======+ | |                  |
|           |          |     | |           |          |     |          |                  | |                  |
|           |          |     | +===========+==========+=====+==========+==================+ |                  |
|           |          |     |                                                              |                  |
+===========+==========+=====+==============================================================+==================+

FlipTablePad 옵션

var person2 = new Person2()
{
    Name = new Name("Junil", null),
    Age = 37
};
Console.WriteLine(person2.FlipTablesFrom(FlipTablesPad.Right));
+====================+=====+
|               Name | Age |
+====================+=====+
| +=======+========+ |  37 |
| | First |   Last | |     |
| +=======+========+ |     |
| | Junil | (null) | |     |
| +=======+========+ |     |
|                    |     |
+====================+=====+


Posted by 땡초 POWERUMC

댓글을 달아 주세요