.NET

VS2010

ScottGu เปิดเผยความสามารถ VS2010 ขึ้นเรื่อยๆแล้วแฮะ ใช้ลงแบบ Side-By-Side กับ Visual Studio 2003 - 2008 ได้ด้วย อยากรู้รายละเอียดเพิ่มลองอ่านได้ที่ Blog ของเค้า คาดว่าเค้าจะเขียนซีรี่ส์ VS2010 กับ .NET 4.0 นี้ไปอีกซักพัก

ที่แปลกใจคือจำได้ว่าอาทิตย์ก่อนลองสั่ง Compatibility Check สำหรับ Windows 7 ดู ปรากฏว่า Visual Studio 2005 กับ Visual Studio 2008 มันไม่ผ่านทดสอบ นี่ยังไม่ได้ลองกับ Visual Studio 2003 เลย ถ้าเกิดมันไม่ผ่านแบบนี้แล้วจะเอามารัน Side-By-Side ใน Windows 7 ยังไงหว่า หรือว่าต้องใช้ Windows XP Mode แต่ถ้าใช้ XP Mode ก็เอามาทดสอบของใหม่ๆที่เอาไว้ใช้กับ Vista ไม่ได้สิ หรือว่า MS จะออก service pack หรือ compatibility pack เหมือนตอน VS2005 กับ Vista หรือว่าก็ใช้ๆไปเถอะ ยังไงก็ไม่น่ามีปัญหาหว่า

เคยคิดมานานว่าถ้ามี Multi-Target Support แล้ว คงไม่ต้องใช้แบบ Side-by-side ล่ะมั้งสำหรับงาน dev ทั่วๆไป แต่ก็ไม่รู้เหมือนกันเพราะไม่เคยมีงานเข้ามาตอนเวลาคาบเกี่ยวแบบนี้เลย ถึงจะมีจริงก็คงจะใส่ไว้ทั้งคู่เพราะถึงจะทำ multi target ตอน compile time ได้แต่ว่าตัว project setting มันก็ดันใช้ข้ามรุ่นกันไม่ได้อีก เมื่อไหร่มันจะทำให้ข้ามรุ่นได้น้า

ส่วนเรื่อง clean web.config สำหรับ ASP.NET นี่ท่าทางจะพึ่งสำนึกว่าไฟล์ config มันใหญ่แล้วน่ากลัวมาก เห็นของใหม่แล้วท่าทางจะเรียบง่ายดี

Joining array of strings in Java

จาก tweet ของ @veer66 เลยอยากรู้ว่าทำไมเลยลองหาดู ดีที่สุดได้แค่นี้

Java

String[] s = "Hello World".split(" ");
StringBuffer sb = new StringBuffer();
for (int i=0;i<s.length;i++){
     sb.append(s[i]);
     if (i != s.length-1)
        sb.append(" ");
}
System.out.println(sb.toString());

ถ้าภาษาอื่นล่ะ

Python (ผ่าน interactive mode)

>>> " ".join("hello world".split(" "))
'hello world'

Ruby (ผ่าน irb)

irb(main):001:0> "Hello World".split(' ').join(' ')
=> "Hello World"

C#

Console.WriteLine(String.Join(" ","Hello World".Split(' ')));

Ruby เท่สุดแฮะรู้สึกว่ามันเป็นธรรมชาติมาก

ตอนที่เขียน Python ใหม่ๆก็รู้สึกอยู่ว่ามันขัดๆ พอเริ่มคิดได้ว่า C# มันก็ไม่มี join ตรงๆเหมือนกัน ถึงนึกออกว่าตอนเขียน C# มันก็รู้สึกแปลกๆเหมือนกัน

อะ แถมด้วยโค้ดตอบ tweet นี้

irb(main):002:0> print 'คิดถึง'*99

Trim leading and trailing silence in wave file

ผมต้องมาทำงานกับ Wave File อีกแล้วล่ะ ทีนี้ปัญหาคือ Wave file ที่ได้จาก SAPI มันมีเสียงเงียบอยู่ตรงทางหัวและท้าย ดังตัวอย่างข้างล่างเป็น wave form ของคำว่า united states ซึ่งไม่เข้าใจว่ามันเป็นเฉพาะ Microsoft Anna รึเปล่า ทำให้ทางหัวมันจะเป็นเสียงเงียบอยู่ซัก 0.05 วินาที ส่วนตอนท้ายจะมีเสียงเงียบอยู่ 0.1 - 0.3 วินาที เวลาเอามาต่อกันหลายๆส่วนแล้วจะฟังเป็นเสียงเงียบไปนานมาก จึงจำเป็นเลยต้องตัดมันออก

united_state

ASP.NET MVC : Controller Overview

เนื่องในโอกาสที่ ASP.NET MVC 1.0 ออกตัวเต็มมาเป็นที่เรียบร้อยแล้ว เราก็ได้โอกาสเขียนถึงเจ้า ASP.NET MVC อย่างจริงๆจังซักที แถมหลังๆมาก็ไม่ได้ใช้ ASP.NET MVC เลย คราวนี้ขอเขียนแบบ Back to basic เพื่อจะช่วยให้เห็นอะไรที่ไม่เคยเห็นบ้าง

Azure hardship

I can say I have some experience with programming for cloud platform. When Google App Engine recently launched, I made Birdnest with Google App Engine. I had nice experience with GAE so I didn't hesitate to apply for Azure when Microsoft annouced the program. In the end, I've got Windows Azure invitation for some time but I don't have chance to play with it until a month ago.

Firstly, Azure does require Vista. Yes. That vista. The OS I never have any intention to install to my machine. I did it for Azure.

Secondly, Azure comes with some documents. More document = better. That's correct. BUT!!! Not in this case. It uses many chapters to explain Azure component. Worker role. blablabla.... They are too much to understand with my tiny brain. It overwhelms a beginner like me. Truthfully, I closed the document, did something else and didn't touch Azure for some weeks. In contrast, look at Google App Engine document. It is a lot easier to understand. Though it is not complete or doesn't explain too much technical thing. It just works.

Lastly, using Windows Azure means a lot of configuration. Microsoft is sardistic. See it yourself in this simple Table Storage walkthrough. Don't be fool by the article name. It is not simple as you thought especially when you come from App Engine. Though Table Storage service is straightforward, it requires too many steps to implement it. I don't know if I can use LINQ with it. This draws me to conclude that I have to write the whole data layer in order to use it.

By the way, I have downloaded some videos to study how to create Azure app. I hope I can learn something useful and makes this blog obsolete.

Routing with Webform

คราวก่อนค้างเอาไว้ว่า ถ้าจะทำ Routing ใน ASP.NET Webform จะทำยังไง มาต่อเลยดีกว่า

เนื่องจาก Routing นั้นเป็นส่วนที่จะเพิ่มเข้ามาใหม่ในตัว Framework ของ ASP.NET 3.5 SP1 นั่นก็หมายความว่าเราสามารถใช้ ASP.NET Routing กับ Web Form ได้เหมือนกับที่เราใช้ความสามารถอื่นๆใน Framework นั่นเอง วิธีที่จะเอามาใช้นั้น Chris Cavanagh ได้ระบุวิธีการไว้ดังนี้

After a little experimentation I found some minimal steps that work pretty well:

  • Create a custom IRouteHandler that instantiates your pages
  • Register new Routes associated with your IRouteHandler
  • That’s it!

ถ้าให้แปลเป็นไทยก็คือ

  • สร้าง class ที่ implement IRouteHandler
  • สร้าง Route ให้ url ที่ต้องการ วิ่งไปที่ IRouteHandler ที่สร้างมาใหม่
  • จบเพียงเท่านี้

URL Routing and Rewriting

นี่เป็นคำถามที่อยู่ในหัวผมหลังจากที่อ่านเอกสารใน MSDNแบบผ่านๆในหัวข้อ ASP.NET Routing versus URL Rewriting พอ M3rlinez มาถามก็เลยลองไปหาอ่านละเอียดๆอีกที พบว่าการทำ Routing (ในบริบทของ ASP.NET) มันต่างกับ URL Rewriting อยู่นิดหน่อย

ข้อแรกที่แตกต่างคือ การทำ Routing รองรับการสร้าง URL โดยใส่พารามิเตอร์ที่ต้องการ ซึ่งตรงนี้ใน URL Rewriting ไม่มีกลไกตรงนี้ให้ต้องทำเอง (มันไม่ต่างกันไม่ใช่เรอะ แค่ไมโครซอฟท์ทำไว้ให้แล้ว) ตรงนี้จะว่าเป็นข้อดีก็ได้

ข้อแตกต่างข้อที่สองคือ Routing ทำงานในอีเวนท์ PostResolveCache และ PostMapRequestHandler ซึ่งจุดนี้แหละที่ต่างจากการทำ URL Rewrite จริงๆ

ปกติแล้วการทำ URL Rewrite จะใช้ HttpHandler หรือใช้ HttpModule มาดักตอนอีเวนท์ BeginRequest ซึ่งเป็นอีเวนท์แรกสุด ซึ่งตรงนี้ URL ที่เกิดรีเควส จะถูกแปลงให้อยู่ในรูปของ URL ที่ ASP.NET Webform ธรรมดาๆ เช่น "/products/15" ก็จะถูกเขียนใหม่เป็น "product.aspx?id=15" แล้วค่อยส่งไปให้ไฟล์ดังกล่าว ดังนั้นอีเวนท์ที่เกิดขึ้นด้านหลังอย่าง AuthenticateRequest จะเกิดหลังจากที่ทำการแปลง URL ไปแล้ว ทำให้การตรวจสอบสิทธิต่างๆทำกับไฟล์ปลายทาง (ซึ่งเป็น Physical file) แทนที่จะเป็นไฟล์ที่เห็นจริงๆ แต่สำหรับ URL Routing แล้วเราสามารถใช้งานระบบ Authentication/Authorization ของ ASP.NET กับ logical file ผ่าน web.config ได้ทันที สิ่งนี้ผมยังไม่ได้ทดลอง แต่สรุปเอาเองหลังจากการดูสายการทำงานของอีเวนท์นะครับ

<location path="products">
  <system.web>
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>
</location>

ผลพวงจากการที่ URL ไม่ได้ถูกเปลี่ยนในระหว่างการทำงานทำให้พารามิเตอร์ action ของอีลิเมนท์ form จะถูกตั้งให้เป็น URL ของเดิมจริงๆ ทำให้เราไม่ต้องใช้ Actionless Form เข้ามาใช้

URL Rewriting ที่ผมเคยคิดว่ามันจะกลายเป็นส่วนหนึ่งของ IIS7 นั้นถูกเปลี่ยนให้เป็น Routing แล้วผนวกเข้ามาใน ASP.NET นั้น จุดสำคัญน่าจะมาจากการที่ทีม ASP.NET นั้นได้เริ่มการพัฒนา ASP.NET MVC แล้วก็เลยจำเป็นต้องพัฒนาส่วน Routing ด้วย แล้วไหนๆก็ไหนๆแล้วก็เลยจับมันแยกออกมาเป็นคอมโพเนนท์เลยดีกว่า เพราะว่า ASP.NET Developer เดิมก็มีจำนวนไม่น้อยที่ต้องทำ URL Rewriting ด้วยตัวเอง

สรุปแล้ว URL Routing ไม่ได้มีอะไรพิเศษพิสดารต่างจาก URL Rewriting ซักเท่าไหร่ แนวคิดเดียวกันเป๊ะ แค่นำส่วนที่เราต้องทำแน่ๆอย่างการใช้ Regular Expression ในการสกัด URL และพารามิเตอร์เพื่อส่งให้ Action/WebForm ที่ถูก และการสร้าง URL รวมเข้ามาอยู่ในตัวเฟรมเวิร์กเลย และจุดประสงค์จริงๆก็คงจะเอามาใช้กับ ASP.NET MVC นั่นแหละ

URL Routing in ASP.NET MVC

ใน ASP.NET MVC เราสามารถออกแบบ URL ขึ้นมาแล้วส่ง request ไปยัง action ที่กำหนดไว้ได้ ทำให้ URL ของเว็บของเราไม่ขึ้นอยู่กับโครงสร้างของไฟล์ที่อยู่บนดิสก์จริงๆ โดย ASP.NET MVC อาศัย URL Routing ที่เพิ่มเข้ามาใน ASP.NET 3.5 SP1

หลังจากเราสร้าง Project Web Application ขึ้นมาแล้ว ลองเปิดดูที่ Global.asax จะเห็นว่าต่างจาก ASP.NET Web Application ธรรมดา เพราะจะมาการทำ Routing มาให้แล้ว

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
        );
    }
    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

กฏง่ายๆของการ Routing คือ

  • หน้าไปหลัง เริ่มทำการทดสอบจากกฏที่เพิ่มเข้าไปก่อนเป็นลำดับแรก เจออันไหนก่อนก็จะ route ไปตามนั้น
  • ใช้ {} เพื่อเก็บค่าตัวแปร เราสามารถตั้งตัวแปรสำหรับ URL ได้ เช่น /blog/{year}/{month}/{day} จะ match กับ /blog/2009/1/2 โดยจะเกิดตัวแปรของ URL ชื่อ year, month และ day ขึ้นโดยมีค่าเป็น 2009, 2 และ 1 ตามลำดับ
  • ใช้ * เพื่อระบุว่าเป็น parameter ที่ไม่ทราบจำนวน

ASP.NET Routing จะต้องมี Routing Handler ซึ่งจะต้องเป็นคลาสที่ implement interface IRouteHandler ซึ่งปกติใน ASP.NET MVC จะแอบใช้ System.Web.Mvc.MvcRouteHandler

จากด้านบน เราสามารถแปลได้ง่ายๆว่า

  • ไม่ต้องสนใจถ้า request url ที่เป็น .axd โดยส่วนที่อยู่หน้า . จะถูกเก็บไว้ในตัวแปรชื่อ request ส่วนที่เหลือทั้งหมดจะถูกเก็บใน pathInfo
  • Request URL จะถูก Route ไปยัง controller และ action โดยระบุพารามิเตอร์ชื่อ id โดยอัตโนมัติหากไม่มีการกำหนดค่าใดๆ

เรามาลองสร้าง Flickr Mashup ขึ้นมาเล่นๆดูดีกว่า

ก่อนอื่นสร้าง PhotoController ขึ้นมาใหม่ โดยมีโค้ดดังนี้

public class PhotoController : Controller
{        
    public ActionResult Index()
    {
        Flickr f = new Flickr();
        Photos p = f.PhotosGetRecent(25, 1);            
        return View(p.PhotoCollection);
    }
}

จากแอคชันข้างต้น เราดึงรายละเอียดของรูปที่ถูกอัพโหลดขึ้นไปไว้ใน Flickr จำนวน 25 รูปแล้วเก็บไว้ใน ViewData โดยมีใช้ โดยใช้ View ง่ายๆ ชื่อ Index คือ

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Photo.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>MVC Flickr Sample</title>
</head>
<body>
    <div>
    <% foreach (var p in ViewData.Model)
       { %>                     
       <a href="<%= p.WebUrl %>">
       <img src="<%= p.SquareThumbnailUrl %>" alt="<%= p.Title %>" />                  
       </a>
    <% }%>
    </div>
</body>
</html>

โดยที่ View นี้เป็น Strong typed view

namespace MvcApplication1.Views.Photo
{
    public partial class Index : ViewPage<FlickrNet.Photo[]>
    {
    }
}

หลังจากเราตั้งค่า Flickr API เรียบร้อยตาม Document ก็ทดลองรันดูด้วย Url /photo/

ScreenShot001

ทีนี้เราลองเพิ่มอะไรขึ้นมาเล็กน้อยด้วยการเพิ่ม Route เข้าไปดังต่อไปนี้

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Search",                                              // Route name
            "search/{key}/{page}",                           // URL with parameters
            new { controller = "Photo", action = "Search", key = "", page = 1 },  // Parameter defaults
        );

        routes.MapRoute(
            "User",                                              // Route name
            "photo/{username}/{page}",                           // URL with parameters
            new { controller = "Photo", action = "PhotoByUser", page = 1 },  // Parameter defaults
        );

        routes.MapRoute(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
        );

    }

จะเห็นว่าเราส่ง Request ไปยัง Action ใดๆ ก็ได้โดยไม่ขึ้นกับชื่อ กล่าวคือ เราจะทำการค้นหารูปหาก Request URL อยู่ในรูปแบบ search/คำค้น/หน้า และหากเป็น /photo/ชื่อผู้ใช้/หน้า ก็จะแสดงรูปของผู้ใช้คนนั้นแทนโดยเราไม่จำเป็นจะต้องสร้าง Search controller แต่อย่างใด แต่เราจะส่งไปที่ action Search และ PhotoByUser ตามลำดับ

โค้ดของ action ทั้งสองเป็นดังนี้

    public ActionResult PhotoByUser(string username, int page)
    {
        Flickr f = new Flickr();
        FoundUser u = f.PeopleFindByUsername(username);            
        if (u != null)
        {               
            Photos p = f.PeopleGetPublicPhotos(u.UserId, 25, page);                
            return View("index", p.PhotoCollection);
        }
        return View("index");
    }

    public ActionResult Search(string key, int page)
    {           
        Flickr f = new Flickr();
        PhotoSearchOptions option = new PhotoSearchOptions();
        option.Page = page;
        option.Text = key;         
        Photos p = f.PhotosSearch(option);                       
        return View("index", p.PhotoCollection);                        
    }

เมื่อเราทดสอบด้วยการเปิด URL /search/grassland/ และ /photo/wiennat จะปรากฎรูปขึ้นมาตามต้องการ

ScreenShot002

แล้วถ้าเราลองเปิดด้วย /search/grassland/ข้อความ และ /photo/wiennat/ข้อความ ล่ะ

ScreenShot004

ผลลัพธ์คือเกิด error ขึ้นเนื่องจากพารามิเตอร์ page นั้นมีค่าเป็น NULL แต่ว่าเราต้องการพารามิเตอร์ที่เป็นตัวเลข เรามีทางแก้สองทางคือ 1. กำหนดให้ประเภทพารามิเตอร์ page ให้เป็น int? 2. จำกัดรูปแบบของพารามิเตอร์ โดยกำหนด Constrain ให้รับแต่ตัวเลข เท่านั้น

    routes.MapRoute(
            "Search",                                              // Route name
            "search/{key}/{page}",                           // URL with parameters
            new { controller = "Photo", action = "Search", key = "", page = 1 },  // Parameter defaults
            new { key="[A-Za-z0-9]+", page="[0-9]*" }
        );

เมื่อผู้ใช้เข้ามาด้วย /search/grassland/ข้อความ ก็จะปรากฏข้อความที่บอกว่าไม่พบเอกสารดังกล่าวแทน

ScreenShot004

เป็นอันจบวิธีการใช้งาน URL Routing อย่างง่ายๆแล้ว สำหรับตอนหน้า เราจะมาดูว่าเราจะใช้ URL Routing ใน Web Form กันอย่างไร

สำหรับการใช้งานอื่นกรุณาทิ้งไว้ใน comment

Updates on IronRuby

Last week I went to Thailand Next Web App and joined in a Rails session. They had a talk and discussed about running Rails in production. Of course, JRuby, Glassfish and other Ruby implementation including IronRuby were mentioned. Then I was asked about it because I was the only .NET guy there. At that time, I didn't follow the IronRuby for a long time so I replied them that no one other than John Lam and Phil Haacked have used the IronRuby.

Few days ago, while I read the feed about asp.net, I found an open source IronRuby project that aims to help developer implement WPF/Silverlight in rails-like fashion. And that's mean we, the developers, now can use IronRuby.

After some googling, I've recalled that IronRuby, like IronPython, is part of the Microsoft Dynamic Language Runtime. They both are released with full source under Microsoft Public License(MS-PL). Though the source code of IronRuby is hosted on RubyForge and IronPython is on the CodePlex, the license doesn't mention that they're open source projects. However, the IronRuby libraries are tended to be open source and are opened to receive the contributions.

From the IronRuby home page, the implementation still does not pass the RubySpecs and can only dispatch some Rails requests. That's mean it still cannot be used to host a Rails application. But you can utilize .NET framework and create an application with it, like IronNails. If you want to give it a try, you have to compile it yourself from the source. Of course, Windows machine is essential.

In TNWA last week, Sirn said "Don't host your rails apps with IIS". I'm sure the situation will change if IronRuby can host Rails app perfectly and I'm waiting for that day.

PS. As for IronPython, I saw how IronPython used with ASP.NET and it was fantastic!! However, I never have chance to try it myself.

The Anatomy of Wave File

เนื่องจากจำเป็นจะต้องทำงานกับไฟล์เสียงก็เลยต้องข้อมูลของไฟล์ wav มานั่งย่อยเพื่อเพิ่มความเข้าใจ หลังจากเข้าใจแล้วก็เลยต้องจดไว้ซักเล็กน้อยเผื่อว่าจะมีใครสนใจ

บล็อกนี้จะไม่อธิบายถึงวิธีการสร้างและรายละเอียดของเนื้อหาของไฟล์ Wave เท่าไหร่นะ แต่จะเน้นหนักไปที่เรื่องของโครงสร้างมากกว่า

พร้อมแล้วก็มาเริ่มได้เลย