Just algorithm!
รู้จัก Linq แบบถึงกึ๋น!!

เมื่อประมาณ 4-5 ปีที่แล้ว มีรุ่นพี่คนนึงซึ่งผมนับถือเป็นเทพ IT Smiley
แนะนำให้ผมรู้จัก (C-Omega)
ซึ่งพี่เค้าอธิบาย Concept ของ C-Omega ให้ฟังว่า
Relational คือ สี่เหลี่ยม, XML คือ สามเหลี่ยม, Object คือ วงกลม
การจัดการข้อมูลต่างกัน ทำให้ไม่เคยรวมกันได้สักที
C-Omega คือ ภาษาที่มีจุดมุ่งหมายให้ ทั้ง 3 รูปแบบรวมเข้าด้วยกันอย่างลงตัวที่สุด

C-Omega เดิมทีชื่อว่า Xen ภายหลังรวมเข้ากับ Polyphonic C# (ทำ Parallel Programming)
จึงเปลี่ยนไปใช้ชื่อ C-Omega
C-Omega นี่แหละครับรากเหง้าของ Linq
ที่ทำให้การทำ Object-Relational Mapping (ORM) ง่ายมาก
แถมเป็น Type-safe คือ ไม่มี Error จากการ assign ค่า Smiley

นอกจากนี้ส่วนที่ทำให้ linq ประสบความสำเร็จ คือ Functional Programming (FP)
ซึ่ง C# เดิมทีเป็นประเภท Imperative Programming ใช่ไหมครับ
เนื่องจากต้องปรับตัวให้เป็น FP จึงเกิด Concept ใหม่ ๆ เช่น
Lambda Expression, Type Inferrence, และ Anonymous Type

เรามาดูกันว่า FP มันมีส่วนช่วย Linq ยังไง
สมมติถ้าเราอยากได้ List ชื่อและที่อยู่ของลูกค้าที่อยู่ในกรุงเทพฯ
ถ้าเขียนด้วย Linq เราเขียนได้ง่าย ๆ ว่า

var cusAddr = from c in customers
              where c.Province == "Bangkok"
              select new { c.Name, c.Address };


แต่ถ้าเราเขียนใน C# 2.0 เราต้องเขียนขนาดนี้ Smiley

CusAddr cusAddr =
    Enumerable.Select<Customer, CusAddr>(
        Enumerable.Where<Customer>(customers,
            delegate(Customer c) {
                return c.Province == "Bangkok";
            }),
        delegate(Customer c) {
            CusAddr result = new CusAddr();
            result.Name = c.Name;
            result.Address = c.Address;
            return result;
        });


ดูแล้วไม่อยากเขียนใช่ไหมครับ แบบนี้ Linq ไม่ได้เกิดแน่ Smiley
แถม Concept ของ Linq คือ ต้องเขียนเป็น Expression
ไม่มีการ assign ค่าเกิดขึ้น ดูในส่วนของ delegate ชุดที่ 2 สิครับ
เราต้องมีการ Assign ค่า Name กับ Address เข้าไป

อย่างนี้เลยเกิด Concept ของ Object initializers กับ Collection initializers ขึ้นมา
คือ ค่าที่เขาต้องการ Assign เนี่ย ก็ประกาศไปตอน Instantiate Class เลย
เขียนใหม่เป็นอย่างนี้ครับ

CusAddr cusAddr =
    Enumerable.Select<Customer, CusAddr>(
        Enumerable.Where<Customer>(customers,
            delegate(Customer c) {
                return c.Province == "Bangkok";
            }),
        delegate(Customer c) {
            return new CusAddr() { Name = c.Name, Address = c.Address };
        });


สั้นลงอีกหน่อย Smiley
ทีนี้ภาษาที่เป็น FP มี Lambda Expression กันเยอะละ
ภาษา C# มีมั่งละกัน จะได้เจ๋งกว่า Java :)
Lamba Expression ก็ไม่มีอะไรหรอกครับ
แค่ตัดคำว่า delegate กับวงเล็บ {} ออก แต่ทำให้อ่านง่ายขึ้นเยอะ
เขียนใหม่เป็นแบบนี้

CusAddr cusAddr =
    Enumerable.Select<Customer, CusAddr>(
        Enumerable.Where<Customer>(customers, (Customer c) => c.Province == "Bangkok"),
        (Customer c) => new CusAddr() { Name = c.Name, Address = c.Address });


แต่ยังดูยุ่ง ๆ อยู่แฮะ
แถมเรื่อง Type inference ไปด้วยละกัน (บรรยายแทน ความคิดของผู้ออกแบบ C# Smiley)
Type inference คือการรับทราบ Type จาก Parameter ที่ส่งเข้ามา กับ Result ทีคืนออกไป
เขียนใหม่เป็นแบบนี้

CusAddr cusAddr =
    Enumerable.Select(
        Enumerable.Where(customers, c => c.Province == "Bangkok"),
        c => new CusAddr() { Name = c.Name, Address = c.Address });


อืม เริ่มสั้นลงละ แต่ประโยคเวลาอ่านมันไม่ Meaningful แฮะ
Customers ต้องเป็น Object หลักสิ
เพิ่ม Extension methods เข้าไปอีกดีกว่า
Extension methods คือการเขียน Methods ที่ทำให้ Object เป้าหมายเรียกใช้ได้
โดยที่เวลาส่งผ่าน Parameter ส่งผ่านแค่ Parameter ที่ 2 เป็นต้นไป
Parameter แรกก็คือ Object เป้าหมายนั่นเอง
Extension method เป็นการทำ Partial function application นั่นเองนะครับ
เขียนใหม่อีกที

CusAddr cusAddr = customers.Where(c => c.Province == "Bangkok")
                      .Select(c => new CusAddr() { Name = c.Name, Address = c.Address });


โอ้วววว เริ่มใกล้ความจริงละ Smiley
แต่ไอ้ Class ที่ขื่อ CusAddr นี่เราต้องเขียน Class เองเลยเรอะ !
ถ้าได้ใช้ครั้งเดียวคงไม่คุ้มที่จะสร้าง Class ขึ้นมา
เพิ่ม Anonymous Type ให้อีกละกัน
Anonymous Type ก็เป็น Type ชนิดหนึ่งไม่มีชื่อ Type มีแต่ Property
เนื่องจากมันไม่มีชื่อ Type
จึงไม่สามารถใช้เป็น Input หรือ Output ของ members ใน Class ได้
แต่ใช้งานใน Block ของ method ได้สบาย
เขียนใหม่เป็น

var cusAddr = customers.Where(c => c.Province == "Bangkok")
                  .Select(c => new { Name = c.Name, Address = c.Address });


โย่ว! สุดท้ายเพื่อความเนียน
เพิ่ม Linq เข้าไป
Linq ก็คือ syntax ต่าง ๆ ที่เปลี่ยนเป็น code ด้านบนให้

var cusAddr = from c in customers
              where c.Province == "Bangkok"
              select new { c.Name, c.Address };


โอ้วววว แจ่มแมว Smiley

ทีนี้เราจะเอา Code ด้านบนไปใช้ใน Query Provider ยังไงล่ะ?
เราก็เพิ่มความสามารถให้ Lambda Expression เนี่ย
สามารถสร้าง Expression Tree ได้สิ :)
Expression Tree คือ Class ที่เก็บข้อมูล Code ของ Lambda นั่นแหละ
เพื่อให้ Query Provider อ่าน เพื่อที่จะได้เอา Code ด้านบนไปแปลงเป็นภาษา SQL

เช่น ถ้าเราประกาศอย่างนี้

Func<int, int> square = x => x * x;

ผลลัพธ์คือจะได้ method ชื่อ square
แต่ถ้าจะสร้าง Expression Tree ให้เขียนอย่างนี้

Expression<Func<int, int>> square = x => x * x;

แค่เติมคำว่า Expression ล้อมรอบ Func
แค่นี้ก็ได้ Object ที่เป็น Expression Tree ชื่อ square มาละ

Expression Tree นอกจากจะบอกโครงสร้างของ Lambda Expression
เรายังสามารถสร้าง Dynamic Code จาก Expression Tree ได้ด้วยนะ เช่น

ParameterExpression paramX = Expression.Parameter(typeof(int), "x");
Func<int, int> square = Expression.Lambda<Func<int, int>>(
                            Expression.Multiply(paramX, paramX),
                            paramX).Compile();


คำสั่ง Compile คือการ สร้าง anonymous method ขึ้นมานั่นเอง

ดังนั้นจุดมุ่งหมายของ Linq จึงแบ่งได้เป็น 2 แบบ หลัก ๆ คือ
1. การเขียน Linq เพื่อทำ FP เช่น Linq to Object (ในอนาคตก็จะมี Parallel Linq อีกแบบ)
2. การเขียนเพื่อแปลงภาษา เช่น Linq to Sql

คราวนี้มารู้จักกับ Syntax ของ Linq กัน
สมมติว่าเราอยากรู้ว่า Sales Rep ของเราแต่ละคน
ขายสินค้าได้ยอดเท่าไหร่กันบ้างในปีนี้
โดยเรียงลำดับจากคนที่ขายได้มากไปน้อย

var result = from s in salesReps
             join o in orders on s equals o.Owner
             where o.Date.Year == 2008
             from d in o.Details
             group d.Amount * d.Price by s into g
             let t = g.Sum()
             orderby t descending
             select new { SalesRep = g.Key, Total = t };


from ถ้าอยู่ตัวแรกจะทำหน้าที่แค่ประกาศว่า จะเริ่มทำการ Query

join ทำหน้าที่ join รายการ 2 รายการเข้าด้วยกัน
โดยต้องมี Key ที่ใช้ร่วมกัน
ในที่นี้ join o in orders on s equals o.Owner
หมายถึง ทำ Inner join กับ orders โดยที่ salesRep ตรงกับ Owner ของ orders

where ทำหน้าที่กรองข้อมูล
ในที่นี้ where o.Date.Year == 2008
หมายถึงเอา orders เฉพาะปี 2008

from ถ้าไม่ได้อยู่ตัวแรก คือการทำ SelectMany
SelectMany มีความหมาย 2 แบบ คือ
1. Cross Join คือ แต่ละรายการในชุดแรก join กับทุกรายการในชุดที่ 2 โดยไม่มีการเลือกในการจับคู่
เช่น {A,B} Cross Join กับ {1,2,3} จะได้ {A1,A2,A3,B1,B2,B3} 
2. Nested query คือการเลือกรายการย่อยใน Query
ต่างกับแบบ Cross Join คือ ด้านหลังของคำว่า in เป็น variable ของ Query
เช่น บรรทัด from d in o.Details
จะเห็นว่าด้านหลังของ in ใช้ variable ของ Query นั่นคือ o
มีหมายความว่า จะดึง details แต่ละรายการใน orders มาคำนวนด้วย

group by คือการจัดกลุ่ม
ในที่นี้ group d.Amount * d.Price by s into g
หมายถึงจัดกลุ่มตาม salesRep โดยที่ เอาเฉพาะเนื้อหาคือ จำนวนเงินรวม (d.Amount * d.Price)

into มี 2 หน้าที่
1. ช่วยในการ Join เช่นทำ Group Join หรือ Left Join
ขี้เกียจอธิบาย เดี๋ยวยาว อ่านต่อ ที่นี่
2. เพื่อเริ่ม query ใหม่
คำสั่ง group by กับคำสั่ง select จะเป็นการจบการ query
แต่ถ้าเราเติม into ด้านหลังของ group by หรือ select
จะเริ่มการ query ใหม่
เช่น ถ้าเรามี query 2 ตัว

var x = from a in set
        select a + 1;
var y = from b in x
        select b + 1;


เราสามารถเขียนต่อเนื่องกันอย่างนี้ได้เลย

var y = from a in set
        select a + 1 into b
        select b + 1;


let คือการประกาศตัวแปรใน query
ในที่นี้คือ let t = g.Sum()
หมายถึงประกาศค่า t โดยที่ t เท่ากับจำนวนเงินรวม [Sum(d.Amount * d.Price)] ของแต่ละ salesRep

orderby คือการจัดเรียง
ในที่นี้ orderby t descending
หมายถึงเรียงจำนวนเงินรวมจากมากไปน้อย
ถ้าไม่เติม descending จะเรียงจากน้อยไปมาก
นอกจากนี้ เราสามารถเรียงต่อเนื่องได้ด้วย Comma เช่น
orderby firstname, lastname
หมายความว่า เรียงตามชื่อ และนามสกุล

สุดท้าย select คือการนำเสนอค่า
ในที่นี้ select new { SalesRep = g.Key, Total = t }
หมายถึงการสร้าง anonymous type ขึ้นมา
โดยที่ type นี้มี Property คือ SalesRep และ Total

เวลาเอา query ไปใช้ก็ใช้ foreach ง่าย ๆ

foreach (var item in result)
    Console.WriteLine(item);


จบละครับ ถึงกึ๋นรึยังครับ Smiley
ถ้าใครสนใจ Linq to Sql
ต้องการเขียน Query Provider ใช้เอง (อย่ามัวแต่รอคนอื่นทำให้นะครับ จงเป็นผู้เริ่ม Smiley)
อ่านต่อ ที่นี่

แต่ถ้าชื่นชอบสไตล์ของ Functional Programming ใน Linq to Object
คราวหน้ามาต่อกับ การเอา Linq ไปใช้กับ FP ผสานพลัง Double Action!! เร็ว ๆ นี้

* อ่านจบแล้ว กรุณา Comment ด้วยครับ เพื่อเป็น Feedback และเป็นกำลังใจ
* บทความนี้คัดลอกได้ แต่ต้องมี Link หรือ Url กลับมาที่หน้านี้ และเมื่อคัดลอกแล้วกรุณา Comment แจ้งด้วยครับ




Create Date : 18 สิงหาคม 2551
Last Update : 23 สิงหาคม 2551 15:46:17 น. 36 comments
Counter : 14874 Pageviews.

 
อยากรู้เลยเข้ามาดูว่ามันคืออะไร แหะๆ อ่านแล้วยังงงค่ะ
ไม่มีความรู้เรื่อง C# เลย แต่ขอบคุณมากค่ะ


โดย: -ทำเป็นเล่นไป- วันที่: 19 สิงหาคม 2551 เวลา:9:23:03 น.  

 
ขอบคุณมากครับ แน่นปึ๊กๆเลย เข้าใจขึ้นเยอะ


โดย: ozze IP: 202.28.179.3 วันที่: 19 สิงหาคม 2551 เวลา:12:32:32 น.  

 
ขอบคุณมากครับ เข้าใจบ้างไม่เข้าใจบ้าง

แต่ก้ได้อะไรกลับไปครับ จะติดตามต่อไปนะครับ


โดย: นิ้วโป้ง IP: 222.123.215.107 วันที่: 20 สิงหาคม 2551 เวลา:23:47:13 น.  

 
อ่านแล้วงงมากเลยค่ะ

แต่ชอบมาก


เหมือนอ่านหนังสือ 1 เล่ม

ปึ้กจิงๆๆๆ


โดย: เดะใหม่ IP: 58.9.104.83 วันที่: 30 กันยายน 2551 เวลา:9:09:54 น.  

 
ยอดมากครับ


โดย: heavyweather IP: 58.137.14.42 วันที่: 24 ตุลาคม 2551 เวลา:15:15:39 น.  

 
เยี่ยมครับ
//greatfriends.biz


โดย: gf mem IP: 203.107.158.162 วันที่: 5 พฤศจิกายน 2551 เวลา:12:04:48 น.  

 
ขอบคุณทุกท่านที่เข้ามาอ่านเช่นกันครับ


โดย: chaowman วันที่: 19 พฤศจิกายน 2551 เวลา:10:18:53 น.  

 
สุด ยอ ปอ รอ (สุดยอดไปเลย)


โดย: changCompe IP: 203.113.85.242 วันที่: 8 ธันวาคม 2551 เวลา:10:58:54 น.  

 
ซูฮกยกนิ้วให้ครับผม


โดย: sinosoi IP: 58.147.74.4 วันที่: 22 ธันวาคม 2551 เวลา:0:52:26 น.  

 
:)


โดย: aswellas IP: 203.147.39.194 วันที่: 10 กุมภาพันธ์ 2552 เวลา:11:21:13 น.  

 
พี่คับผมเอาไปเป็นเนื้อประกอบในสัมมนานะครับ

ทำเรื่องนี้แต่เนื้อหามันมีไม่พอเลยต้องขอใช้

ตรงส่วนSyntax ของ Linq ครับผม

ขอขอบคุณมากๆคับ


โดย: ping IP: 202.28.64.1 วันที่: 19 กุมภาพันธ์ 2552 เวลา:19:39:26 น.  

 
เยี่ยมมากครับ เข้าใจขึ้นเยอะเลย


โดย: tukky IP: 58.10.103.238 วันที่: 20 กุมภาพันธ์ 2552 เวลา:21:37:49 น.  

 
ดีมากเลยค่ะ

แต่ตอนนี้ยังติดปัญหาตรง where คือ ตอนนี้สร้างสร้าง textboxserch ขึ้นมาหนึ่งอัน เพื่อไว้ทำการค้นหาข้อมูลจาก datagridview พอดีว่ามาใช้ linq ไม่ทราบว่าต้องใช้ where ในการค้นหาข้อมูลยังไงค่ะ

ช่วยตอบให้หน่อยได้มั๊ยค่ะ



โดย: kwan IP: 124.121.15.177 วันที่: 9 มีนาคม 2552 เวลา:15:58:00 น.  

 
ไม่นึกว่าจะมีบทความภาษาไทยที่เนื่อหาปึกเช่นนี้เลย

ขอบคุณสำหรับบทความดีๆ ครับ


โดย: Rux IP: 58.137.14.42 วันที่: 12 มีนาคม 2552 เวลา:18:19:03 น.  

 
ขอบคุณครับรอภาคต่อไปๆอยู่ครับ


โดย: wait IP: 203.154.9.29 วันที่: 13 พฤษภาคม 2552 เวลา:12:45:51 น.  

 
ถ้าต้องการรู้จัก LINQ เพิ่มเติมลองอ่านที่นี้ดูครับ
//www.theblogfor.net/post.aspx?id=63180f80-2ff4-4abc-ad46-05db15bd1cc9


โดย: win IP: 58.9.236.44 วันที่: 3 มิถุนายน 2552 เวลา:22:19:27 น.  

 
ดีมากๆ เลยครับ ขอบคุณมากครับ


โดย: Prepro IP: 118.172.2.151 วันที่: 18 มิถุนายน 2552 เวลา:8:51:57 น.  

 
ดีครับ น่าจะดี กำลังศึกษาอยู่ครับ


โดย: ปอ IP: 124.121.174.210 วันที่: 1 กันยายน 2552 เวลา:14:20:42 น.  

 
เยี่ยมเลยครับ :)


โดย: aswellas IP: 203.147.39.194 วันที่: 13 ตุลาคม 2552 เวลา:23:10:00 น.  

 
กว่าผมจะ search เจอ บล็อค นี้
เขียนได้ถึงใจจริงๆ ครับ


โดย: BoysBee IP: 127.0.0.1, 124.120.121.170 วันที่: 14 ตุลาคม 2552 เวลา:15:40:52 น.  

 
สุดยอดครับ....


โดย: top@ffx IP: 61.90.149.29 วันที่: 16 พฤศจิกายน 2552 เวลา:17:35:24 น.  

 
เยี่ยมครับ


โดย: theseven IP: 113.53.58.162 วันที่: 4 มีนาคม 2553 เวลา:16:30:21 น.  

 
ขอบคุณสำหรับบทความดี ๆ นะคะ มีประโยชน์มากเลยค่ะ


โดย: nan_ IP: 111.84.27.87 วันที่: 19 มิถุนายน 2553 เวลา:18:07:15 น.  

 
เยี่ยมไปเลยครับ ผมกำลังศึกษาเรื่องนี้อยู่พอดี.... ได้ติดตัวไปแระครับ


โดย: hellish_kevin IP: 125.27.31.100 วันที่: 30 กันยายน 2553 เวลา:6:48:13 น.  

 
Thanks a lot!


โดย: Guess IP: 58.137.14.42 วันที่: 2 มีนาคม 2554 เวลา:11:05:38 น.  

 
คัดลอกโดยการปริ้นมา แปะไว้หัวเตียงครับ !!!!

ว่าแต่ Type inference มีคำอธิบายเพิ่มเติมไหมครับ พอดี ผมไม่ค่อยจะคุ้นกับอะไรที่มันไม่ประกาศ ชนิดของตัวแปรน่ะครับ ยิ่งไปเจอ dynamic ผมยิ่งเตลิดไปใหญ่


โดย: supachoke_boy@hotmail.com IP: 101.108.16.232 วันที่: 22 เมษายน 2554 เวลา:12:13:55 น.  

 
แถวๆ Expression Tree ออกงงๆครับ
ว่างๆถ้ามีบทความเกี่ยวกับพวกนี้
จะขอบคุณมากเลย หุหุหุ
เพราะหาอ่านที่อื่น ก็ใช่ว่าจะมีอธิบายละเอียดๆ


โดย: supachoke_boy@hotmail.com IP: 101.108.16.232 วันที่: 22 เมษายน 2554 เวลา:12:26:08 น.  

 
เก่งมากๆๆ


โดย: owtee IP: 124.121.42.148 วันที่: 30 สิงหาคม 2554 เวลา:14:35:52 น.  

 
ขอบคุณหลายเด้อ


โดย: ไก่ IP: 182.52.250.54 วันที่: 14 กันยายน 2554 เวลา:16:54:24 น.  

 
ขอบคุณมากครับ เยี่ยมมากๆ


โดย: k IP: 203.99.252.249 วันที่: 22 มกราคม 2556 เวลา:15:39:22 น.  

 
สุดยอดเลยค่ะ
ขอบคุณค่ะ


โดย: Nws_nax IP: 180.180.122.130 วันที่: 27 พฤศจิกายน 2556 เวลา:9:40:40 น.  

 
ที่สุด


โดย: yugisang IP: 183.89.150.171 วันที่: 20 ธันวาคม 2556 เวลา:11:53:45 น.  

 
ดีสุด ๆ ถึงกึ๋นมาก ๆ


โดย: gusavoice IP: 183.89.146.124 วันที่: 21 มกราคม 2557 เวลา:10:18:46 น.  

 
ได้ความรู้เพิ่มเยอะเลยครับ
กำลังศึกษาอยู่พอดี


โดย: Geastano IP: 103.10.228.35 วันที่: 10 พฤศจิกายน 2557 เวลา:16:36:22 น.  

 
ยอดเยี่ยมครับ


โดย: Zerrat IP: 223.27.201.198 วันที่: 13 ตุลาคม 2558 เวลา:16:48:04 น.  

 
in คืออะไรครับ แล้วตัวแปรหลัง from คืออะไร????
พอดีกำลังศึกษาครับเลยงงๆหน่อย


โดย: ยอดเยี่ยม IP: 184.82.29.19 วันที่: 4 มิถุนายน 2561 เวลา:13:46:44 น.  

ชื่อ :
Comment :
  *ใช้ code html ตกแต่งข้อความได้เฉพาะสมาชิก
 

chaowman
Location :
กรุงเทพฯ Thailand

[Profile ทั้งหมด]

ให้ทิปเจ้าของ Blog [?]
ฝากข้อความหลังไมค์
Rss Feed
Smember
ผู้ติดตามบล็อก : 8 คน [?]





New Comments
Group Blog
 
All Blogs
 
Friends' blogs
[Add chaowman's blog to your web]
Links
 

 Pantip.com | PantipMarket.com | Pantown.com | © 2004 BlogGang.com allrights reserved.