잊지 않겠습니다.

View는 WebApplication의 Request가 Response로 바뀌는 BlockBox가 된다.

MVC에서 View는 완전한 출력을 담당하는 책임을 가지고 있다. 단지 Controller의 출력만을 담당하며, 간결한 표현 로직을 사용하여 그 출력을 최종 HTML로 Render하는 임무를 가진다. 그렇지만, 표현로직과 업무로직사이의 경계는 매우 모호하다. 업무로직과 표현 로직에 대한 것은 조금은 주관적이라고 생각이 되기 때문에.. 최대한 Controller에서 동작을 하고 표현 부분에 대해서는 로직의 최대한도로 줄이는 것을 목표로 하는 것이 가장 좋다.
또한, View의 문제는 Test에 있다. Html을 테스트하는 것은 GUI 프로그램에서 사용자 액션을 테스트하는 것과 동일하다. 테스트 할 방법도 마땅하지 않으며, 표시된 HTML을 TEXT단위로 테스트 하는 것은 공백 문제부터 시작해서 많은 어려움이 있기 때문에, 많은 책들의 저자와 비슷하게 VIEW는 봐서 잘 되는 것이 최고다. 라는 생각이 든다. 딱히 테스트 할 방법도 안보이는 것이 사실이다.;

WebForm View Engine
: MVC Framework는 WebFormViewEngine이라는 class로 구성된 View Engine을 사용하고 있다. 이 ViewEngine은 기존의 ASP .NET WebForm Engine을 기반으로 구축되어있다. 그러나 WebFormViewEngine에서 View는 단순한 HTML Template이다. 이는 Controller에서 받은 특정 데이터인 ViewData와 강력히 형식화된 Model 객체를 기반으로 만들어진 HTML 문자를 표현하는 기능만을 가지게 된다. 또한 내부적으로 사용되는 기반 기술은 ASP .NET WebForm 서버 페이지 기술과 동일하다.만약에 자체적인 View Engine을 만들고 싶다면, IViewEngine과 IView interface를 상속받아 구현하면 된다.

View의 데이터 전송
: ViewData라는 Dictionary<string, object> 형식의 데이터 또는 강력한 형식의 Model을 이용하는 방법이 있다. 두 방법의 차이는 Controller에서는 전혀 알 필요가 없다. 단지 Controller에서 데이터 형식의 Model을 전송해서 View로 전달 될때, 강력한 Model 형식의 View의 경우에는 형변환이 가능한지만을 알아보게 된다. 따라서 이 방법은 매우 유용한 것이 된다.

ViewData.Eval()의 이용
: ViewData.Eval()은 대소문자를 가리지 않고, 값이 없는 경우에 String.Empty를 리턴하는 사용하기 편한 함수이다. 또한 Eval함수는 .으로 구분되는 토큰 명명법을 사용하고 있기 때문에 Model의 속성또한 표현할 수 있다. ViewData에 key를 Name으로 넣었는지, name으로 넣었는지. 아니면 값이 없는 경우에 NULL처리를 해주는 것을 빼먹지 않았는지 신경쓰지 않고 사용하기에 매우 좋은 함수이다. 또한 Html Helper method에서 Html.TextBox("comment")를 사용하는 경우, 이 코드는 다음과 같다. <input name="comment" id="comment" type="text" value="<%=Html.Encode(ViewData.Eval("comment"))%>"/>. 편하게 작성도 가능하고, 값이 없는 경우에 나오는 모든 에러처리를 해주기 때문에 간결한 코드 작성이 가능하다.
Posted by Y2K
,
.NET으로 COM+ 객체를 만드는 예제.

.NET으로 COM+객체를 만들때는 먼저 Project Property에 Comvisible(true)로 설정이 되어 있어야지 된다. 그리고 assembly:Guid 속성에 component guid를 등록시켜야지만 com 객체를 만드는 가장 기초적인 설정이 끝이 난다. 이와같은 설정을 하면 작성된 dll이나 exe를 com+ 객체로 등록할 수 있다.

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3308202e-a355-4c3b-805d-b527d1ef5fa3")]

그러나, dll을 등록한다고해서 library안의 class들이 모두 com객체로 사용할 수 있는 것이 아니다. dll은 여러개의 com객체를 담을 수 있는 상자이고, 그 상자안에 채워야지 될 com객체들은 각각 작성되어야지 된다. com을 이용하는 것은 com 객체 뿐 아니라, com 객체를 구성하고 있는 interface역시 사용할 수 있어야지 되기 때문에, com으로 외부에서 노출될 모든 interface와 class들은 Guid Class Property를 갖고, 각 Guid 값을 가져야지 된다.

com으로 사용되기 위해서, class와 interface가 가져야지 될 속성은 각각 다른데, interface는 ComInterfaceType Property와 Guid를 가져야지 되며, class는 Com으로 제공될 ComSourceInterfaces, ClassInterface, Transaction, Guid, Com 객체 사용에서 Pool과 CreateTimeOut을 제한하는 ObjectPooling attribute를 가져야지 된다.

com interface 구현 예제
#region Interfaces

[Description("ICSSimpleObject description")]
[Guid("2A59630E-4232-4582-AE47-706E2B648579")]
// The public interface describing the COM interface of the coclass 
public interface ICSSimpleObject
{
    #region Properties

    float FloatProperty { get; set; }

    #endregion

    #region Methods

    [Description("HelloWorld description")]
    string HelloWorld();

    void GetProcessThreadID(out uint processId, out uint threadId);

    [Description("DoTransaction description")]
    void DoTransaction();

    #endregion
}

[Guid("A06C000A-7A85-42dc-BA6D-BE736B6EB97A")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
// The public interface describing the events the coclass can sink
public interface ICSSimpleObjectEvents
{
    #region Events

    [DispId(1)]
    void FloatPropertyChanging(float NewValue, ref bool Cancel);

    #endregion
}
#endregion

com class 구현 예제
// The ObjectPooling attribute is used to configure the component's object 
// pooling. It can enable or disables object pooling and set the min. or  
// max. pool size and object creation timeout. 
[ObjectPooling(MinPoolSize=2, MaxPoolSize=10, CreationTimeout=20)]
// Creates the component with a new transaction, regardless of the state 
// of the current context.
[Transaction(TransactionOption.Required)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ICSSimpleObjectEvents))]
[Guid("14EAD156-F55E-4d9b-A3C5-658D4BB56D30")]
public class CSSimpleObject : ServicedComponent, ICSSimpleObject
{
    #region Properties

    /// 
    /// The private members don't make it into the type-library and are 
    /// hidden from the COM clients.
    /// 
    private float fField = 0;

    /// 
    /// A public property with both get and set accessor methods.
    /// 
    public float FloatProperty
    {
        get { return this.fField; }
        set
        {
            bool cancel = false;
            // Raise the event FloatPropertyChanging
            if (null != FloatPropertyChanging)
                FloatPropertyChanging(value, ref cancel);
            if (!cancel)
                this.fField = value;
        }
    }

    #endregion

    #region Methods

    /// 
    /// A public method that returns a string "HelloWorld".
    /// 
    /// "HelloWorld"
    public string HelloWorld()
    {
        return "HelloWorld";
    }

    /// 
    /// A public method with two outputs: the current process Id and the
    /// current thread Id.
    /// 
    /// [out] The current process Id
    /// [out] The current thread Id
    public void GetProcessThreadID(out uint processId, out uint threadId)
    {
        processId = NativeMethod.GetCurrentProcessId();
        threadId = NativeMethod.GetCurrentThreadId();
    }

    public void DoTransaction()
    {
        try
        {
            // Operate on the resource managers like DBMS
            // ...
	
            ContextUtil.SetComplete();  // Commit
        }
        catch (Exception ex)
        {
            ContextUtil.SetAbort();     // Rollback
            throw ex;
        }
    }

    #endregion

    #region Events

    [ComVisible(false)]
    public delegate void FloatPropertyChangingEventHandler(float NewValue, ref bool Cancel);

    /// 
    /// A public event that is fired before new value is set to the
    /// FloatProperty property. The Cancel parameter allows the client 
    /// to cancel the change of FloatProperty.
    /// 
    public event FloatPropertyChangingEventHandler FloatPropertyChanging;

    #endregion
}
Posted by Y2K
,
모든 Controller는 IController interface를 구현한다.

* Mvc.Controller의 특징
1. ActionMethods
  : Controller의 동작이 여러개의 method로 분할된다. 각 Action method는 서로다른 URL로 노출되며,
  들어오는 요청에서 추출된 매개변수를 가지고 있다.
2. ActionResult
  : Action의 의도된 결과를 나타내는 개체를 선택해서 반환할 수 있다. 결과를 지정하는 것과 실행하는
  것이 분리되기 때문에 자동화된 테스트가 상당히 간단하게 구성될 수 있다.
3. Filter
  : 재사용 가능한 동작들을 Filter로 캡슐화할 수 있다.

* Controller의 입력 처리
Mvc.Controller에서 사용 가능한 속성들
1. Request.QueryString : 요청과 함께 전송된 GET 변수들
2. Request.Form : 요청과 함께 전송된 POST 변수들
3. Request.Cookie : 요청과 함께 전송된 Cookie
4. Request.HttpMethod : 요청을 위해 사용된 Http method
5. Request.Headers : 요청과 함께 전송된 HTTP Header
6. Request.Url : 요청된 Url
7. Request.UserHostAddress : 요청을 보내온 사용자의 Ip Address
8. RouteData.Route : 요청에 대해 선택된 RouteTable.Routes Entry
9. RouteData.Values : 현재의 Route 매개 변수(URL에서 추출된 값이거나 기본 값)
10. HttpContext.Application : Application state 저장소
11. HttpContext.Cache : Application cache 저장소
12. HttpContext.Items : 현재 요청에 대한 상태저장소
13. User : 로그인 한 사용자의 인증정보
14. TempData : Session 내에서 이전 HTTP 요청을 처리하는 동안에 저장된 임시 Data

* Controller의 출력 처리
a. View를 Rendering하는 것으로 HTML 반환
b. HTTP 재전송 호출(311, 312 code 반환)
c. 응답의 출력 스트림에 다른 데이터 전송(xml, json, file)

* Action method 안에서의 직접적으로 Response를 다루는 일은 절대로 하지 않아야지 된다. 대신에 특정한 응답을 나타낼 수 있는 ActionResult를 상속한 객체를 반환하도록 한다.

* ActionResult 형식들
1. ViewResult : 지정된 View Template이나 기본 View Template을 Rendering 한다. [Mvc.Controller.View() method]
2. PartialViewResult : 지정된 Partial View Template이나 기본 Partial View Template을 Rendering한다.(Mvc.Controller.PartialView()]
3. RedirectToRouteResult : Routing 구성 설정에 따라 URL을 생성하면서 302 재전송을 하게 한다.[Mvc.Controller.RedirectToAction()]
4. RedirectResult : 임의의 URL로 HTTP 302 재전송을 하게 한다. RedirectToRouteResult와 다른 점은 RedirectResult는 외부의 URL을 사용가능하다는 차이점을 가지고 있다.
5. ContentResult : 브라우저로 text 데이터를 반환할 수 있다. 선택적으로 Content Header를 지정 가능하다.
6. FileResult : 이진 데이터를 브라우저로 직접 전달한다.
7. Jsonresult : .NET 객체를 Json 포멧으로 직렬화하고 이를 응답으로 전송한다.
8. JavascriptResult : 브라우저에 의해 실행될 수 있는 javascript 소스코드의 일부를 전송한다.
9. HttpUnauthorizedResult : Http 응답코드를 401로 설정한다.
10. EmptyResult : 아무 일도 하지 않는다.

사용자 지정 ActionResult를 만들고 싶은 경우(Image를 가공해서 반환한다던지 등의 Action이 가능하다)에는 ActionResult를 상속받아서 ExecuteResult를 재정의해서 사용하면 된다. 다음은 그 예제 코드이다.


public class WatermarkedImageResult : ActionResult
{
    public string ImageFileName { get; private set; }
    public string WatermarkText { get; private set; }

    public WatermarkedImageResult(string imageFileName, string watermarkText)
    {
        ImageFileName = imageFileName;
        WatermarkText = watermarkText;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        using(var image = Image.FromFile(ImageFileName))
        using(var graphics = Graphics.FromImage(image))
        using(var font = new Font("Arial", 10))
        using(var memoryStream = new MemoryStream())
        {
            var textSize = graphics.MeasureString(WatermarkText, font);
            graphics.DrawString(WatermarkText, font, Brushes.White, 10, image.Height - 10 - textSize.Height);

            image.Save(memoryStream, ImageFormat.Png);
            var response = context.RequestContext.HttpContext.Response;
            response.ContentType = "image/png";
            response.BinaryWrite(memoryStream.GetBuffer());
        }
    }
}

* TempData와 Session과의 비교
기본적으로 TempData의 저장소는 Session이 된다. 그렇기 때문에 TempData를 사용하고 싶으면 반드시 Session을 enable 시켜야지 된다. 하지만 TempData의 고유한 특징은 이것이 매우 작은 데이터라는 것이다. 각 entry는 오직 한번의 이전 요청을 저장하고 난 다음 없어진다. 처리 후에 자동으로 청소가 되니 RedirectToAction()에 걸쳐 개체를 유지하는데 적합하다.

* 재 사용가능한 Filter 처리하기
Filter를 이용해서 Controller와 ActionMethod에 추가적인 동작을 지정할 수 있다.
Filter는 요청 처리 PipeLine에 별도의 단계를 추가하는 .NET Attribute로 Action method의 실행 전후에, ActionResult가
실행 전후에 추가적인 로직을 삽입할 수 있다.

Action method의 실행 전후는 IController.Execute method의 실행 전, 후를 의미하며,
ActionResult의 실행 전후는 ActionResult.ExecuteResult method의 실행 전, 후를 의미한다.
따라서 실행 순서는 OnActionExecuting, OnActionExecuted, OnResultExecuting, OnResultExecuted 순서로 실행된다.

ex) HandleError : HandleErrorAttribute는 Controller의 Action method가 실행될 때에 예외가 발생되면 HandleError에 지정된 View가 Render된다.이때에 web.config에 customError mode가 on이 되어있어야지 된다. 이때 지정된 View는 HandleErrorInfo data model이 전달되며, 이는 불려진 Controller와 ActionName, Exception의 정보를 담게 된다. 이 상태는 Error를 처리된 상태로 만들게 되며 따라서 Http 응답코드는 에러가 아닌 200이전달되게 된다.


Controller Test Code
* Controller의 Unit Code는 의미있는 단위 테스트를 작성하기 위해서 많은 사람들이 AAA Pattern을 따른다. Arrange-Act-Assert로 이루어지는
이 방법은 대부분의 Controller 테스트에 유용하다.

[TestClass]
public class HomeControllerTest
{
    [TestMethod]
    public void CheckViewName()
    {
        HomeController controller = new HomeController();
        ViewResult viewResult = controller.Index();

        Assert.IsNotNull(viewResult);
        Assert.AreEqual(viewResult.ViewName, "index");
    }

    [TestMethod]
    public void CheckViewData()
    {
        HomeController controller = new HomeController();
        ViewResult viewResult = controller.Index();

        Assert.IsNotNull(viewResult);
        Assert.AreEqual(6, viewResult.ViewData["age"]);
    }

    [TestMethod]
    public void CheckRedirection()
    {
        HomeController controller = new HomeController();
        RedirectToRouteResult result = controller.Index();

        Assert.IsNotNull(result);
        Assert.AreEqual("RedirectActionName", result.RouteValues["action"]);
    }
}
Posted by Y2K
,