:: Kunagorn Sirikup :: C# Developer

22/5/56

Action Filter with HTTP-Compression


ในการรับส่งข้อมูลโดยทั่วไปเริ่มต้นจากการเกิด HTTP Request ข้อมูลไปยัง Web Server ซึ่งถ้า Request มีขนาดใหญ่ก็จะเกิดปัญหาตามมาก็คือใช้เวลาในการทำงานนาน อีกวิธีที่ช่วยลดขนาดของ Request  ก็คือ HTTP-Compression พูดง่ายๆก็คือ การบีบอัดข้อมูลให้มีขนาดเล็กลงก่อนที่จะทำการส่ง Request  ไปยัง Web Servers จึงช่วยลดขนาดของ Bandwidth และเพิ่มประสิทธิภาพความเร็วในการรับ-ส่งข้อมูลระหว่าง Web Servers และ Clients ซึ่งมีรูปแบบของการ Compress 3 รูปแบบ คือ GZip , Compress , Deflate ในส่วนของการพัฒนาด้วย C# ต้อง Run ผ่าน Internet Information Service (IIS) ซึ่งรองรับทั้ง GZip  ละ Deflate ตัวอย่างนี้จะลองประยุกต์ใช้ HTTP-Compression ผ่านการทำ AcionFilter ของ MVC นะครับ

เริ่มแรกสร้างโปรเจค MVC ขึ้นมา สร้าง Folder Filters สร้าง Class ชื่อ CompressFilter ในส่วนบนสุดให้ทำการ  using System.Web.Mvc; เพื่อให้สามารถ Inherit ActionFilterAttribute ได้ ดังรูป


จากนั้นจะได้ให้เขียน Code ดังนี้

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO.Compression;

namespace MvcApplication1.Filters
{
    /// <summary>
    /// Compresiion Filter Request
    /// </summary>
    public class CompressFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpRequestBase request = filterContext.HttpContext.Request;
            string acceptEncoding = request.Headers["Accept-Encoding"];
            if (string.IsNullOrEmpty(acceptEncoding)) return;
            acceptEncoding = acceptEncoding.ToUpperInvariant();
            HttpResponseBase response = filterContext.HttpContext.Response;
            if (acceptEncoding.Contains("GZIP"))
            {
                response.AppendHeader("Content-encoding", "gzip");
                response.Filter = new GZipStream(response.Filter
                                                 , CompressionMode.Compress);
            }
            else if (acceptEncoding.Contains("DEFLATE"))
            {
                response.AppendHeader("Content-encoding", "deflate");
                response.Filter = new DeflateStream(response.Filter
                                                 , CompressionMode.Compress);
            }
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
        }
    }
}

เท่านี้เราก็สร้าง HTTP-Compression เสร็จเรียบร้อย แต่เมื่อ Run ทดสอบแล้ว จะเกิด Request ดังรูป



สังเกตว่ายังไม่มีการ Compress เกิดขึ้น โดยดูจาก Request ยังมีขนาดเท่าเดิมเพราะว่าเรายังไม่ได้กำหนด CompressFilter ให้กับ Action ที่ต้อการการ Compress ในตัวอย่างนี้จะทดสอบโดยกำหนดที่ Index ใน Controller Home โดยเขียน Code ดังนี้

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication1.Filters;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        [CompressFilter]
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }
}

เมื่อ Run ทดสอบแล้วสังเกตว่าขนาดของ Request จะเล็กลงจาก 1.3 KB เหลือ 853B และมีเวลาในการทำงานน้อยลงจาก 22 ms เหลือ 20 ms



20/5/56

Loading and save data with Knockout

วันนี้ขอแนะนำเรื่อง Knockout นะครับ เป็น Library ขนาดเล็กที่ช่วยให้งานสร้าง dynamic UI ง่ายขึ้น ด้วย (Model-View-ViewModel) MVVM Pattern ประกอบด้วยส่วนหลักๆคือ Declarative Binding, Automatic UI Refresh, Dependency Tracking, และ Templating น่าสนใจดีนะครับ

เริ่มแรกสร้างโปรเจค MVC ขึ้นมาก่อน จากนนั้น Reference Knockout.js

<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js" 
        type="text/javascript"></script>


จากนั้นเขียน HTML ดังนี้

<h3>Loading and save data with Knockout</h3>

<form data-bind="submit: addTask">
    Add task: 
    <input data-bind="value: newTaskText" 
        placeholder="What needs to be done?" />
    <button type="submit">Add</button>
</form>

<ul data-bind="foreach: tasks, visible: tasks().length > 0">
    <li>
        <input type="checkbox" data-bind="checked: isDone" />
        <input data-bind="value: title, disable: isDone" />
        <href="#" data-bind="click: $parent.removeTask"
             class='button-list'>Delete</a>
    </li
</ul>

<br /><br /> 
You have <data-bind="text: incompleteTasks().length">&nbsp;</b>
incomplete task(s)
<span data-bind="visible: incompleteTasks().length == 0"></span>

ในตัวอย่างผมได้สร้าง CSS ไว้แสดงหน้าจอด้วย เพราะอยากให้ปุ่ม Delete ในตัวอย่างออกมาในรูปแบบที่ต้องการ

<style>
    html,body{ background:#ffffff;}
    
    .button-list
    {
        text-decoration:none;
        background:#696969; 
        border:1px soid #000000; 
        font-size:11px; 
        color:#ffffff!important; 
        padding:3px 8px 3px 8px;
    }
</style>

จากนั้นเมื่อ Run จะได้หน้าจอ ดังนี้


ตอนนี้หน้าจอจะยังไม่สามารถทำงานได้ ต้องเขียน Javascript นี้ก่อน เพื่อกำหนดส่วนของ Data และ Opeation ดังนี้

<script>
    function Task(data{
        this.title ko.observable(data.title);
        this.isDone ko.observable(data.isDone);
    }

    function TaskListViewModel({
        // Data
        var self this;
        self.tasks ko.observableArray([]);
        self.newTaskText ko.observable();
        self.incompleteTasks ko.computed(function ({
            return ko.utils.arrayFilter(self.tasks(),
                function (task{
                    return !task.isDone(
            });
        });

        // Operations
        self.addTask function ({
            self.tasks.push(new Task({
                titlethis.newTaskText(
            }));
            self.newTaskText("");
        };
        self.removeTask function (task{
            self.tasks.remove(task
        };
    }

    ko.applyBindings(new TaskListViewModel());</script>

จากนั้นเมื่อ Run แล้วกรอกข้อมูลที่ TextBox กดปุ่ม Add จะทำงานโดยบันทึกข้อมูลแล้วโหลดแสดงรายการ



credit : http://learn.knockoutjs.com/#/?tutorial=loadingsaving

17/5/56

Improving Performance with Output Caching


Output Caching เป็นอีกวิธีหนึงในการเพิ่มประสิทธิภาพของ ASP.NET MVC โดย Output Cache จะช่วยให้ Content ที่ถูก Return จาก Controller Action ที่มีเนื้อหาเดียวกันจะไม่ถูกสร้างทุกครั้ง

ยกตัวอย่าง ASP.NET MVC ต้องการแสดงข้อมูลจาก Database ผ่าน View Index โดยทั่วไปต้องมีการ Execute เพื่อดึงข้อมูลที่ Controller Action  แล้ว Retun ข้อมูลมาที่ View Index เท่ากับว่าจะมีการ Retrieve ข้อมูลทุกครั้ง ซึ่งบางทีข้อมูลที่ Retrieve  มาไม่ได้มีการเปลี่ยนแปลงทำให้เกิดการทำงานซ้ำซ้อนบน Server

ในการเพิ่มประสิทธิภาพ ASP.NET MVC จึงใช้ Ouput Cache เพื่อหลีกเลี่ยงการ Execute Database ทุกครั้ง ดังนั้นเมื่อผู้ใช้ Request ไปที่ Controlller Action เดิม View index จะ Retrieve ข้อมูลจาก Cache แทน

Enabling Output Caching
เราสามารถใช้งาน Output Caching ได้โดยการกำหนด Attribute [OutputCache] บน Controller Action

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [OutputCache(Duration=10, VaryByParam="none")]
        public ActionResult Index()
        {
            return View();
        }

    }
}

จาก Code ข้างบนจะเป็นการกำหนดว่าถามีการเรียก Controller Action Index จะมีการเก็บ Cache โดยมี Parameters คือ Duration บอกว่าเราต้องการเก็บข้อมูล Cache ไว้กี่วินาที ในตัวอย่างจะมีการเก็บ Cache ไว้ 10 วินาที ส่วน VaryByParam ช่วยให้เราสามารถสร้าง Cache Version จาก Parameter หรือ Query String ที่ต่างกัน มีค่า Default เป็น "none" โดยมี Parameters ต่างๆดังนี

   Location="Any | Client | Downstream | Server | None | ServerAndClient "
   Shared="True | False"
   VaryByControl="controlname"
   VaryByCustom="browser | customstring"
   VaryByHeader="headers"
   VaryByParam="parametername" 
   VaryByContentEncoding="encodings"
   CacheProfile="cache profile name | ''"
   NoStore="true | false"
   SqlDependency="database/table name pair | CommandNotification"