[C#] มาดูกันว่า User Remote เข้ามาเปิดใช้ Application จากเครื่องไหน

ก่อนอื่นมาดูปัญหากันก่อนครับ ว่าทำไมถึงต้องไปไล่ดูว่า มีการ Remote จากเครื่อง User เพื่อมาใช้งาน Application ที่ติดตั้งบนเครื่อง Server หรือป่าว ? เพราะ IT ของลูกค้า อยากรู้ว่ามีใครใช้งานในระบบบ้าง แล้วใช้งานที่เครื่องไหน แต่บังเอิญว่ารอบนี้ ตัว Application ที่พัฒนาถูกไปติดตั้งบรเครื่อง Windows Server พอ User Remote เข้ามาใช้งาน ปรากฏว่าระบบลง Log และ Active User มาจากเครื่อง Serer ที่เป็น Windows Server ทั้งหมดเลยครับ โดยคำสั่ง C# ที่ใช้ดึงว่าเครื่องที่ Run Application อยู่ ชื่อเครื่องอะไร เป็นคำสั่งที่มีใน .Net Framework API ตามตัวอย่าง Code ด้านล่างเลยครับ

มาที่โจทยฺ์ใหม่บ้าง เพราะ คำสั่ง Environment.MachineName; มันมีข้อจำกัด ถ้าเอา Application ไปดิดตั้งบน Windows Server มันจะไม่รู้ว่า User Remote มาจากเครื่องไหนครับ คำสั่งนี้จะดึงเฉพาะ แต่ชื่อเครื่อง ของ Server แทน โดยโจทย์ที่ทาง IT ของลูกค้าต้องการ ในกรณีที่มีการ Remote เข้ามาใช้งาน Application มีรูปแบบ ดังนี้ครั

ชื่อเครื่องปลายทาง(ชื่อเครื่องต้นทาง)

หลังจากรู้โจทย์แล้ว มาดูดีกว่าครับ ว่า C# ไม่สิต้องบอกว่า .Net Framework ได้เตรียมอะไรมาช่วยไว้บ้างครับ

  • ตัว System.Windows.Forms.SystemInformation.TerminalServerSession เป็นตัวที่บอกว่า Application ที่เขียนขึ่น Run ผ่่าน Remote Desktop หรือป่าวครับ ถ้าใช่ Return True ถ้าไม่ใช่ Return False ครับ
  • ตัว Remote Desktop Services API – เอาไว้ช่วยดึงค่าบางอย่าง มาจาก Protocal RDP ครับ

Code ที่ได้ปรับปรุง

  • Method GetComputerName() อันนี้เป็น Method ที่เราเปิดให้คนภายนอกใช้นะครับ
  • Helper GetTerminalServerClientNameWTSAPI และ WTSQuerySessionInformation เอาไว้ช่วยดึงค่าที่ต้องการจาก RDP Service ครับ

ผลการทดสอบ

  • เมื่อลองเปิด Application ผ่าน Remote Desktop มันแสดงขึ้น ตาม Requirement ที่ตกลงกันไว้ข้างต้น ดังรูป

Reference

[C#] ตั้งชื่อเล่นของ Class (Alias) ให้เรียกใช้งานได้ง่าย

Reference: https://pixabay.com/en/colored-pencils-colour-pencils-star-374771/

เวลาเราพัฒนาระบบที่มันซับซ้อนๆมากๆ ปัญหาที่ปวดหัวที่สุดเลย คือ การตั้งชื่อ บางทีชื่อตัวแปรยาวๆ ถ้าย่อไป อาจจะงงได้ Dev เลยใช้ชื่อเต็มไป พอลองเอาตัวแปรเหล่านี้มาใช้ มันดูเกะกะรกตามากครับ กว่าจะรู้ว่า ตั้งชื่อเล่นของ Class (Alias) ให้เรียกใช้งานได้ง่ายกันดีกว่า โดย C# มันมีวิธีในการการตั้งชื่อเล่น(Alias) กันนะ มาดู Code เดิมกันดีกว่า

ถ้าสังเกตุใน Code พบว่า RebalanceProcessExtensions.Col_OriginalNAV ชื่อ Class RebalanceProcessExtensions มันยาวมากๆ ครับ ถ้าทำไปนานๆ Code ตรึมครับ ดูยาก ที่นี้มาลองใช้ตัวช่วยกันดีกว่า ลองใช้ Class Alias โดยการย่อชื่อ Class ไว้ช่วง using ครับ โดย Code ที่ได้จะเป็นประมาณนี้

จาก Code ใหม่ ย่อจาก RebalanceProcessExtensions ไปเป็น RBExt แล้ว Code ที่เขียนออกมา สั้นลง และดูสบายตาขึ้น ครับ

[Dapper] Query Parent-Child อย่างไร ?

หลังจากลองทำ Framework ที่เอา Dapper เป็น ORM ครับ ตอนนี้ก็ติดปัญหาว่า เราจะดึงข้อมูลที่อยู่ในรูปแบบ Parent-Child กันอย่างไรครับ โดยผมมีรายละเอียด ดังนี้

  • โจทย์ที่ผมต้องการ คือ เวลา Query ข้อมูลจาก Table Corporateaction แล้ว ให้มันดึงข้อมูลของ Table ลูก CorporateDetail ขึ้นมาให้หมด โดยการ Query เพียงคร้งเดียวเท่านั้นครับ
  • POCO Class ของผม มีโครงคร่าวๆ ดังนี้ครับ
    • Parent Class
    • Child Class
  • Query ที่ผมเขียนไว้
  • Code ในส่วนของ Data Access ผมเขียนลักษณะนี้ครับ
  • เดี๋ยวมาอธิบาย Code ของ Data Access กันก่อนครับ สังเกตุว่าจริงๆ มันก็ Query มาทั้งหมดครับ แต่ใช้ Feature Data Mapping กับ Dictionary มาช่วยจัด Data ให้อยุ่ใน Format ที่ต้องการครับ

สำหรับ Blog นี้เกิดจากการไปขุด Unit Test ของ Dapper ครับ จาก Dapper/Dapper.Tests/MultiMapTests.cs ซึ่งนั้นหมายความว่า ถ้า Library มีเป็นลักษณะ Open Source ถึงแม้ว่าเราจะไม่เจอคู่มือ แต่เราสามารถไปดู Unit Test เพื่อมาศึกษา และประยุกต์ใช้ง่ายได้ครับ ^___^

[C#] Arithmetic Overflow กับคำสั่ง checked และ unchecked

พอดีช่วงนี้ลองทวนสอบ เพื่อเตรียมสอบ Cert MCSD App Builder ของ Microsoft ครับ แล้วบังเอิญไปเจอ keyword อันนึงที่ กูรเขียน Code มาหลายปี ไม่เคยได้ยินเลย checked และ unchecked ครับ โดยก่อนเข้าไปรู้ว่า 2 คำนี้ มันมีความหมายอย่างไรใน C# เรามารู้จักกับ Exception ชนิดหนึ่งกันก่อนดีกว่าครับ

Arithmetic Overflow/Underflow คือ อะไร ?

สำหรับคอมพิวเตอร์เวลามันทดอะไรบางอย่าง เราจะเก็บที่หน่วยความจำ แล้วที่นี่ในการเขียนโปรแกรมตอนที่เราจะทดค่าต่างๆนั้น มันมีภาชนะให้เก็บหลายรูปแบบ ซึ่งพื้นฐานของคอมพิวเตอร์มาจากคณิตศาสตร์ การที่เราจะเก็บอะไรลงภาขนะ แต่ละแบบ มันมีข้อจำกัด นัั่นก็ช่วง หรือขอบเขตที่สามารถรับได้ ถ้าสรุปในภาษา Dev มันบอกว่า Data Type(ภาชนะ) ซึ่งแต่ละแบบ เช่น int, float Decimal หรือ String มันมีข้อจำกัด หรือขอบเขต ที่บอกว่าเราสามารถเก็บอะไรลงไปได้ และได้เท่าไหร่ มี min-max นั้นเองครับ ถ้าลองดูใน C# ค่า min และ max ของ DataType บางชนิดมา มีค่า min/max นะครับ ดังนี้ครับ

แล้ว Overflow กับ Underflow  คือ อะไรหละ ?

  • Overflow คือ การคำนวณต่างๆ แล้วผลลัพธ์ที่ได้มันมีค่ามากกว่าที่ตัวแปรชนิดนั้นๆ จะเก็บค่าได้ครับ (สั้นๆ ค่าเกิน max)
  • Underflow คือ การคำนวณต่างๆ แล้วผลลัพธ์ที่ได้มันมีค่าน้อยกว่าที่ตัวแปรชนิดนั้นๆ จะเก็บค่าได้ครับ (สั้นๆ ค่าน้อยกว่า min)

OverflowException คือ อะไร ?

Exception ข้อผิดพลาด ที่เราสามารถจัดการได้ สำหรับในกรณีนี้ คือ ข้อมูลที่คำนวณได้ มันล้นเกิน(Overfllow) ออกมาจากจากภาชนะที่เก็บไว้ (ตัวแปร) ถ้าดูโครงสร้าง OverflowException  พบว่ามันเป็น Type หนึ่งของ System.ArithmeticException ครับ

checked & unchecked

หลังจากรู้จักกับ Arithmetic Overflow Exception แล้ว กลับมาที่ตัว C#กันก่อนครับ ว่าตัวภาษามันมีอะไรช่วยเรา หรือป่าว คำตอบ คือ มีครับ แต่ปกติไม่เปิดไว้ (ค่า Default = unchecked) แต่มีคำสั่งเข้ามาจัดการเรื่องนี้ 2 คำสั่งครับ ได้แก่

  • checked – บอกว่า Code ที่อยู่ในส่วนของ คำสั่ง checked มีการตรวจสอบ พวก Overflow/Underflow ครับ ถ้าดูจาก Code เป็น Code ที่ผมใช้คำนวณดอกเบี้ยของพันธบัตร/หุ้นกู้(Bond) ครับ
  • unchecked – บอกว่า Code ที่อยู่ในส่วนของ คำสั่ง unchecked ไม่มีการตรวจสอบ พวก Overflow/Underflow ครับ อ้าวแล้วกรณีไหนที่ต้องไม่ Check หละ การคำนวณด้าน การเงิน คงไม่ใช่แน่ๆ เงินหาย คงไม่มีใครยอมอยู่แล้วครับ แต่มีบางอัลกอริทึมนะ ที่ไม่ได้สนใจค่าของมัน(magnitude) อย่าง เช่น การทำ Hash ครับ

Note:

  • สำหรับตอนนี้ใน C# ยังไม่มี UnderflowException ครับ ถ้าเกิด Exception ในกรณีนี้จริงๆ ระบบจะโยนตัว InvalidOperationException มาแทนครับ
  • คำสั่ง checked / unchecked  มี ขอบเขตแบบ Local ถ้าในขอบเขต ดันไปมีการเรียกใช้ Method อื่น ตัว Method อื่น ไม่เข้าข่ายนะครับ ตามตัวอย่าง พบว่า Method CalcSomethingOverflows ไม่เข้าขอบเขตของคำสั่ง checked ครับ
  • คำสั่ง try-catch มันก็ไม่ได้สนใจพวก Overflow และ Underflow ครับ
    • จากตัวอย่าง Code สังเกตุว่า Method dependsonDefault ปล่อยให้คำตอบเป็นไปตามยถากรรมครับ ซึ่งสามารถทำงานได้ด้วย แต่ถูก หรือป่าวอีกเรื่องนะครับ แต่หากมีคำสั่ง Checked ไว้ ตามใน Method checkedMethod มันจะ throw exception ทันทีครับ
    • ผลลัพธ์การ Run ครับ

โห ถ้าจะต้องการให้มันตรวจสอบทั้ง Project นีไม่ต้องไล่ใส่คำสั่ง Checked ไปจนพรุนทั้ง Solution/Project เลยเหรอ คำตอบ คือ ว่ามีครับ ถ้าให้ในมัน Defaultให้มีค่าเป็น checked ตั้งแต่ต้นให้ไปกำหนดค่าใน Project ดังรูปครับ

ถ้าตรงไหน ไม่เอาใช้คำสั่ง unchecked มากำหนดแทนครับ

Reference

 

 

[C#] How to Get property name from object

งานของผมใช้ Dapper แล้วปัญหามันมีอยู่ว่าตอนสร้าง predicate(เงื่อนไข) มันต้องเอาชื่อ Property ไป Where ทำให้มันมีความเสี่ยงมาก ถ้าแก้ชื่อ Property แล้ว Refactor ไม่ครบครับ ผมเลยทำ Helper มาเพื่อช่วยให้มันไป Get ชื่อ Property จากตัว Object ซะเลยครับ ได้มาเป็น

แบบที่ 1: Get Property with create an instance

ตัวอย่างการใช้งานครับ

Trade-off

ปัญหาของแบบแรก คือ ต้องมีการ new Object ขึ้นมาครับ เสีย Cost มาแก้เป็นแบบที่สองดีกว่า

แบบที่ 2: Get Property without create an instance

ตัวอย่างการใช้งานครับ

จบไปแล้วกับ Blog ในสาย Coding ที่ทิ้งห่างไปนานพอสมควร หลังจากที่เริ่มเรียน ป โท ครับ

 

[C#] ลองใช้ Log4Net มาเก็บบันทึกของ Console App กัน

หลังจากสร้าง draft ทิ้งไว้นานมาแล้ว วันนี้มาลองรื้อบทความเก่าๆมาเขียนให้เสร็จครับ สำหรับ Blog ตอนนีเป็นการลองใช้ Log4Net กับ console app ครับ เอาหละมาลุยกันเลยครับ

เตรียมตัว & ติดตั้ง

  • สร้าง new project แบบ console app
  • ติดตั้ง Apache log4net จากตัว nuget คลิกขวาที่ Project >> Manage Nuget Package >> Search “Nuget” >> Install ครับ

สร้าง Configuration file

  • สร้างไฟล์ไว้ที่นี่
  • สำหรับตัว Console App ลองเอา Config นี้มาใช้เลย
  • มาอธิบาย Log ดีกว่า โดยดูจากตัว Appender เป็นตัวที่บอกให้ log ว่าแสดงผลที่ไหน ทั้ง console, textfile หรือลง DB เป็นต้น มีการ Config อย่างไรครับ
    • ColoredConsoleAppender – แสดงผลบนหน้า Console App ครับ โดยผมมีการ Map สีของ Log กับรูปแบบ ดังนี้
      • ERROR – สีแดง
      • WARN – สีเหลือง
      • ALL – สีเขียว
    • RollingFile – เขียนลง File ตาม path ที่กำหนดครับ โดยขนาดของ Log จะไม่เกิน 1 MB ครับ ถ้าเกินจากนั้นก็จะแยกไฟล์ และเก็บ log ล่าสุดไม่เกิด 10 ไฟล์ครับ
  • มาดูอีกส่วนใน config ส่วนของ root เป็นตัว Config ทำให้ Log ทำงานครับ
    • อันนี้ผมเรียกใช้ทั้ง ColoredConsoleAppender และ RollingFile

เชี่อม config กับ Console App

  • อย่าลืม Config ของ Log4Net ครับ กำหนด “Copy to Output Directory” เป็น “Copy if newer” หรือ “Copy Always”
  • ทำให้ Console App ของเรา ดึงตัว Config ของ Log4Net ขึ้นมาครับ
    • แก้ไขไฟล์ AssemblyInfo.cs ใน property เดิม Code นี้ลงไปครับ (ระวัง Path ของไฟล์ log4net.config.xml)

  • ประกาศตัวแปรของ Log

  • ลองเรียกใช้ Log ตาม Code ตัวอย่างครับ อย่างผมลอง TestException แบบ Throw กับ Rethrow ครับ

  • มาดูผลลัพธ์กันครับ ฝั่ง Console
  • มาดูผลลัพธ์กันครับ ฝั่ง Log file
  • ลองกลับไปดู Log จาก Console เก็บชนิด Log | เวลา | Log Message + Class + stack track และ อื่นๆ ถ้ามี
  • สำหรับ Code ลองดูได้จาก Git เลยครับ

 

[C#] มาใช้ HtmlAgilityPack ดึงข้อมูลจากเว็บ

ตอนที่ผมเขียน Blog น่าจะเป็นช่วงปลายปี 2016 นะครับ พอดีวันนี้เหลือเวลาอีก 2-3 ชั่วโมงจะเลิกงาน ผมเลยลองทำ Project ง่ายๆ ดูครับ โดยดึงข้อมูลจากเว็บ เกี่ยวกับข้อมูลพื้นฐานของหุ้นนะครับ ก่อนที่จะทำ สิ่งที่ตต้องทำลอง Research Library ดูก่อนครับ ว่ามีอะไรช่วยได้ไหม เท่าที่ดูมี Library ที่เหมาะสม ดังนี้ครับ

  • HtmlAgilityPack สามารถ Download มาจาก Nuget ได้เลยครับ

ถึงเวลาเริ่มทำ

  • ต้องไปส่องเว็บก่อน ว่าเราดึงข้อมูลอะไร
  • ลองดู Code ของ HTML ที่ได้ ลอง View Source ครับ
  • สิ่่งที่ผมสนใจ คือ ข้อมูลงบการเงิน/ผลประกอบการ ตามนี้ครับ
  • หลังจากเข้าใจ Pattern แล้ว ลองลุยเขียน Code เลย สำหรับผมที่โง่ฝั่งเว็บมากๆ ขอเป็น Winform ครับ ลากๆแปะๆเสร็จ

ลองมาดูวิธีใช้ HtmlAgilityPack ดึงกว่า

  • ก่อนใช้ HtmlAgilityPack เราต้องมีข้อมูลเว็บก่อนครับ สำหรับผมใช้ HttpWebRequest กับ HttpWebResponse เข้ามาช่วยครับ ลองดู Code ตัวอย่างได้เลย
  • เมื่อมีข้อมูลแล้ว ต่อมาเป็นหน้าที่ของ HtmlAgilityPack ผมต้องโยนข้อมูลจาก Setup แรกเข้าไป มันมี 2 แบบ
    • Load – ส่ง Stream หรือ ส่ง file path เข้าไปก็ได้ครับ
    • LoadHtml – โยน String เว็บเข้าไปเลยครับ (ผมใช้วิธีนี้แหละ ลองดูตัวอย่าง Code ด้านล่างเลย

  • เนื่องจากเว็บอาจจะใหญ่เกินไป เรามาใช้ลองข้อมูลในการค้นหาลงครับ อย่างผมกำหนด Criteria เป็น

    ลองสังเกตุ HTML ส่วนที่ผมสนใจในข้างต้นครับ จากนั้นก็ตัดมันออกมาครับ

  • คราวนี้มาลองใช้ HtmlAgilityPack โดยผมสนใจเฉพาะ Tag “Table” นะครับ ผมใช้ตัว doc.DocumentNode.SelectNodes(“//table”) เพื่อให้ได้ตัว HtmlNode ครับ โดยตัว HtmlNode มองว่าเป็น Graph ครับ มันเก็บข้อมูล และมีความสัมพันธ์ไปยัง Node อื่นๆครับ

  • ก่อนหน้านี้ foreach คราวนี้ลองดึงมาทั้งยวงครับ ตาม Code เลย สังเกตุดีนะครับ ผมดึงมาทั้ง Tag
    และเลย

  • เนื่องจากมันเป็น C# มันใช้ Linq ได้ด้วยครับ อย่างของผมจะเอา Attribute ชื่อ height ที่มีขนาด 15 และ 30 ครับ โดย Method ที่ใช้ คือ GetAttributeValue เป็นของ HtmlNode ครับ

  • Property ที่น่าสนใจ สำหรับที่ผมลองใช้นะครับ
    • ChildNode – บอกว่ามี Node ลูกไหม เช่น <tr> มี <td> เป็น node ลูก
    • InnerHtml – ข้อมูล HTML ของ Node นั้น
    • InnerText – ข้อมูล Text ที่อยู่ภายใน Tag HTML

ลองใช้งานจริง

  • ใส่รายชื่อลงไป จากนั้นกด Grab ครับ

Source Code

  • อยู่ที่นี้ครับ (ไม่รับประกันว่าสามารถ Run ใช้งานได้นะครับ ลงดูเหตุผลได้จาก หัวข้อสรุป)

สรุปสิ่งที่ได้ทำ

  • เข้าใจการทำงานของ HtmlAgilityPack ครับ แต่มันมีข้อเสียอย่างนึง คือ ไม่ค่อยมี Document ครับ ต้องลองหาๆจาก Google => StackOverflow ครับ
  • การดึงข้อมูลจากเว็บยากนะครับ ถ้าดึงแล้ว ถ้าเว็บมีการใส่ Tag แปลกๆมา หรือปรับเปลี่ยนโครงสร้าง Code อาจจะพังได้เลย
  • ใช้ DataTable ของ C# นี้ก็มืนเหมือนกันนะ ปกติใช้ List<Object-DTO> เป็นหลัก
  • บทความนี้ทำ เพื่อการศึกษาครับ

[C#] Guidelines for Exception Handling

หลังจากเขียน Code ในการทำงานมาหลายปี พอดีเจอบทความนึงที่น่าสนใจ ผมเลยทำความเข้าใจ และสรุป เกี่ยวกับ Exception ดังนี้ครับ

Exception คือ อะไร ?

  • คือ การแจ้งข้อมูลข้อผิดพลาดที่เกิดขึ้น จากการทำงานของระบบครับ มันมีกลุ่มที่เราสามารถจัดการเองได้ ใช้ Try Catch เข้าช่วย หรือจัดการไม่ได้เลยพวกกลุ่ม Error

Guidelines for Exception Handling

  • Catch only the exceptions that you can handle. – เวลาเขียน Code ใน Method หรือ Class ให้จัดการ Exception ที่น่าจะเกิดขึ้นได้กับชิ้นงานที่ทำอยู่ และพยายามแจ้งกับ User ดูภาษาที่เข้าใจง่าย ส่วน Exception นอกเหนือจากนี้ ปล่อยให้ Caller เป็นคนจัดการ เข่น ถ้าทำ Component ในการอ่านไฟล์ สิ่งที่เราสนใจ
    • ทำ Component อ่านไฟล์ กลุ่ม Exception ควรเป็นกลุ่ม IO
    • ถ้าเกิด Expception จริง เช่น ไฟล์มันถูกอ่านอยู่ หลังจาก Exception เกิดขึ้น สิ่งที่เราควรทำ คือ แจ้ง Message ที่ User สามารถเข้าใจง่าย และเลือกที่ทำอะไรต่อไป
    • ไม่จำเป็นต้องดัก Exception แปลก เช่น StackOverflowException ถ้าเกิดขึ้นจริง สิ่งที่ทำได้ คือ Throw(โยน) ไปให้คนที่มีหน้าที่จริงๆจัดการ
  • Don’t hide (bury) exceptions you don’t fully handle. – ใช้ Catch เท่าที่จำเป็น บางเรื่องเราไม่จำเป็นต้องรับมาหมด โยน(Throw) ให้คนที่เกี่ยวข้องจัดการดีกว่า อย่าซ่อน Exception เพราะ คิดว่า User จะตกใจ เช่น
    • ทำ Process B เกี่ยวกับคำนวณอยู่ Exception ที่เราต้องใช้น่าจะมี ArithmeticException, DividebyZeroException  เป็นต้น
    • เมื่อตอน Runtime มันเกิด Expception OutofMemory ขึ้นมา
    • สิ่งที่เราควรทำ คือ โยนให้ Process A ที่เรียก B มาทำงาน จัดการ Exception อย่าซ่อนมันไว้ (ถ้าซ่อนไว้ เราอาจจะเจอเคสที่อยู่ App ปิดตัวเองแบบเงียบๆ ไม่แจ้งอะไร)
  • Use System.Exception and general catch blocks rarely. – พยายามอย่าดักจับ Class Exception ตรงๆ ใช้ให้น้อยที่สุด อาจจะใช้ในกรณีที่ Exception ที่ถูกโยนเข้ามามันไม่มีทางไปแล้ว เพื่อให้ Exception แสดงออกมา
    • Catch Exception จากเล็กไปใหญ่เสมอ ตาม Code ดังนี้
    • จาก Code นี้ ผมเรียก Exception ดังนี้
      • ด่านแรก DividebyZeroException เพราะถูก Extend มาจาก ArithmeticException
      • ด่านสอง ArithmeticException เพราะถูก Extend มาจาก SystemException
      • ด่านสุดท้าย Exception – Mother of Exception เป็น Class แม่ของเหล่า Exception เลย – ใช้เท่าที่จำเป็น เพราะ ปกติแล้ว เวลาที่เราเขียน Code ตัว IDE มันจะ Hint Exception มาให้ หรือ ถ้าถึกหน่อยไปดูใน MSDN
  • Use throw; rather than throw <exception object> inside a catch block.
    • มารู้จักกับ throw และ throw <exception object> กันก่อน
    • throw <exception object> มาดู Code กันเลย

      • อันนี้ คือการ Throw Exception เปลี่ยน call stack  ของโปรแกรม โดย call stack จะมาสุด ณ ตำแหน่งที่เรา throw exception ออกมา
      • ซึ่งเมื่อเกิด exception ขึ้นจะทำให้เรา trace ปัญหาที่เกิดได้ยากขึ้น เพราะไม่รู้ว่าเกิดขึ้นที่ตรงไหน
    • throw หรือ Rethrow มาดู Code กันเลย

      • การ Rethrow exception ออกมาโดยที่ไม่ได้เปลี่ยน call stack ดังนั้นถ้าเราต้องการที่จะ throw exception เดิมออกมา ให้ใช้ throw
    • ตัวอย่าง Code ของ Throw vs Rethrow ได้ที่ Git ครับ
    • ใช้คำสั่ง throw; สำหรับ Exception ในกลุ่มเดียวกัน เพื่อรักษา call stack เดิมไว้ ส่วนถ้า Exception คนละชนิดให้ดูที่ข้อสุดท้าย
  • Avoid exception reporting or logging lower in the call stack.
    • ต้องย้อนกลับไปข้อที่แล้วก่อน ระหว่าง throw(Rethrow) กับ throw ex
      • throw ex – Call Stack เปลี่ยน
      • rethrow – Call Stack ไม่เปลี่ยน
    • หลังจากเข้าใจ throw กับ rethrow แล้วมาดูการเขียน Log หรือ Report ให้ User ทราบกันดีกว่า ว่าควรทำอย่างไร
      • แบบ rethrow – การเขียน Log หรือ Report ควรจะเขียนใน High Stack (ไปเขียนเอาตอนสุดท้ายสุดเลย ไม่ต้อง Log ตอน rethrow) เพื่อป้องกันการเขียน Log ซ้ำซ้อน
      • แบบ throw – ต้องดูลำดับของ Call Stack ด้วย ถ้าเขียน Log ไม่ดีข้อมูลหายไปนะครับ
    • System.Console.WriteLine() ไม่ต้องพ่นออกมานะ เพราะใน WebApp กับ WinApp พ่นไป ก็ไม่มีประโยชน์
  • Avoid exception conditionals that might change over time.
    • อย่าเอา Exception Message มาเป็นเงื่อนไข หรือ ใช้ exception conditionals ของ C#6 ถ้าไม่มั่นใจว่า Business Logic มันจะนิ่ง หรือ Message นิ่ง ให้ระวังเรื่องภาษาด้วย เพราะ Lib บางตัว อาจจะมีการทำ custom exception แยกตาม แต่ละภาษาทำให้ดักมากกว่าเดิมอีก
    • *สำหรับ exception conditionals – ส่วนตัวมองว่า มันควรใช้ในตอนท้ายสุด ใช้ในการดักจับ Error Code หรือ Message เพื่อแจ้งคำอธิบาย หรือ การแก้ไขเบื้องต้นให้ User มากกว่า
  • Avoid throwing exceptions from exception conditionals.
    • ต่อจากข้อที่แล้วครับ ถ้าเราใช้ exception conditionals มากรอง Exception แล้วโยนต่อไป ปัญหา คือ ฝั่งที่รับ Exception มาจัดการ จะไม่รู้ว่ามันมีสาเหตุมาอย่างไร และควรจัดการอย่างไรครับ
  • Use caution when rethrowing different exceptions.
    • ระมัดระวังในการ rethrowing สำหรับ Exception ที่แตกต่างกัน การไปปรับเปลี่ยนชนิดของ Exception มันทำให้ Call Stack หายไปครับ ใน .Net มีวิธีการแก้ปัญหามาแล้ว คือ การใช้งาน Inner Exception เอาไว้เก็บที่มาของ Exception ล่าสุดที่เกิดขึ้นครับ
    • ตัวอย่างของ Inner Exception ลองดูตามตัวอย่างผมนะครับ อันแรก
      • เรากำลังเขียนไฟล์อยู่ พบว่าไฟล์ไม่มี เลย Throw FileNotFoundException
      • สิ่งที่ทำให้เกิด FileNotFoundException มาจาก Argument ที่ส่งเข้ามาผิด (จาก Code ไม่ส่งมาเลย และไฟล์ไม่มีอยู่จริงด้วย)
      • สิ่งที่ระบบสร้าง FileNotFoundException โดยมี ArgumentException เป็น Inner Exception เพื่อมาบอกสาเหตุที่แท้จริงครับ
      • ลองดู Code กันเลยย

    • มาตัวอย่างที่สอง
      • A ทำการ Process คิดเลขสักอย่าง A มีแต่การคำนวณ ดัก Exception arithmeticexception, DividebyZeroException
      • A เรียก B มาประมวลผล B ไปอ่าน TextFile อ้าวคนอื่นใช้อยู่ Throw Exception มา IOException
      • ใช้ InnerException สิ โดยเป็น A – arithmeticexception และมี ฺB – IOException เป็น inner exception
    • สำหรับสถานการณ์อื่นๆ ลองอ่าน Guideline นี้แล้วลองไปปรับใช้ ครับ
      • Changing the exception type clarifies the problem.
        • ปรับชนิดของ Exception ให้เหมาะสม เพื่อให้สามารถวิเคราะห์ปัญหาได้ง่ายขึ้นครับ
      • Private data is part of the original exception.
        • ข้อมูล Exception บางอย่าง อาจจะมีข้อมูลที่สำคัญกับระบบ เราควรห่อหุ่มมันให้เรียบร้อย
      • The exception type is too specific for the caller to handle appropriately.
        • บาง Exception มันเหมาะสมที่จะโยนขึ้นไปอยู่แล้ว เช่น Error Code ของ Database มันมีความชัดเจนอยู่ในตัว อย่าทำให้มันหายไปครับ เก็บไว้ใน inner exception ครับ

สำหรับผมแล้วบทความนี้สามารถนำไปปรับใช้ได้กับหลายๆภาษาครับ แต่อาจจะต้องเข้าใจ พื้นฐานของแต่ละภาษาก่อนครับ

Reference

[.NET] ประกาศให้โลกรู้ว่า Method นี้ Class นั้น ตกยุคแล้ว !!!

จาก Code เล็กๆ เมื่อวันเวลาผ่านไป มันก็เติบโตขึ้น ผ่านการตัดต่อพันธุกรรมมาอย่างโชกโชน พร้อมกับภาระหน้าที่อันยิ่งใหญ่ แต่บาง Class บาง Method มันไม่ตอบโจทย์ทางธุรกิจแล้ว มันควรจะถูกกำจัดไปซะ แต่ Code มันบางส่วนมันใช้อยู่

เราจะทำอย่างไร เพื่อให้ทีม Develop ที่พัฒนาด้วยกัน เปลี่ยนไปเรียกใช้ Class หรือ Method ใหม่แทน !!!

ทางแรก เราอาจจะประกาสแจ้งให้ในทีมทราบ แต่บางครั้งอาจจะมีหลงลืมบ้าง เพื่อป้องกันปัญหาระยะยาวทำแบบ Java ดีกว่า ตัว Java มันมีบอกว่า Code ส่วนไหนที่เลิกใช้ (deprecated) ไปแล้วครับ โดยในตัว .Net Framework มี Attribute ให้เรียกใช้ครับ คือ ตัว Obsolete ครับ สำหรับตัวอย่างมาลองดูจาก Scenario ดังนี้ครับ

  • ขั้นแรก – เจรจาก่อน โดยการใส่ Attribute Obsolete ไว้บน Method ที่เราต้องการ ในที่นี้ คือ ShiftWorkingDate

    This slideshow requires JavaScript.

  • ขั้นสอง – กระชับพื้นที่ เพิ่ม Message แจ้งเตือน

    This slideshow requires JavaScript.

  • ขั้นสาม – ขอคืนพื้นที่ หมดเวลาแล้ว กำหนด Flag เป็น true ครับ ถ้า Developer คนไหนยังไม่แก้ แก้เวลา Compile มัน Error ครับ

    This slideshow requires JavaScript.

สุดท้ายและ ทำไมถึงต้องมาเขียน เพราะ จาก Code เก่าที่ Migrate มา Method ShiftWorkingDay ที่ทำหน้าที่เลื่อนวันไปในกรณีที่เป็นวันหยุด มันมีถึง 6 Edition ใครอ้างผิด ก็ซวยไปครับ กว่าจะตามมาแก้ได้ใช้เวลาพอสมควรเลย

[C#] ทำไม int / int แล้วไม่มีทศนิยมหละ

พอดีผมได้ Port Code ในส่วน Business Logic อันนึงจาก VB6 มาเป็ C# แทน เพราะดูแล้วเขียนใหม่น่าจะรีด Performance ได้ดีกว่าครับ หลังจากผมใช้เวลาไปประมาณ 1 เดือน ในการยก Code ขึ้นมา Complile ผ่านนะ แต่ Run แล้ว ประกอบกับ UnitTest แล้วผลลัพธ์ผิด พอลอง Debug เข้าไปดูแล้วพบว่า

  • C# int หาร int ได้ 0
  • VB6 ได้ค่าทศนิยมมาครับ

ถึงตอนนี้เราต้องไปดูที่ C# Specification ครับ โดยตัวภาษา C# แบ่งเรื่อง Division operator เป็น 3 กลุ่มครับ

  • Integer Division
  • Floating-point Division – ตามหลักของ IEEE 754 arithmetic
  • Decimal Division

พออ่านมาถึงจุดนี้แล้ว ทำไม Microsoft ไม่ทำให้  Integer division แล้วได้ทศนิยมออกมาเลย หละ สำหรับผม ขอให้ความเห็น ดังนี้ครับ

  • ความเร็ว พื้นฐานคอมพิวเตอร์ที่เราเรียนตอนนี้ยังเป็นระบบ Bit เลข 0 และ 1 ถ้าไม่สนใจทศนิยมมันทำให้กระบวนการคิดสั้นลงมาก (Simple & Fast)
  • ไม่ต้องการสนใจพวก Rounding เพราะ ถ้าเราต้องการให้ Integer Division มีทศนิยมให้ได้เนี่ย สิ่งที่เราต้องเจอการ Rounding ครับ จากเหตุผลข้อที่แล้วคอมพิวเตอร์ไม่ได้คิดเลขแบบคนคิดแบบ 0 และ 1 และถ้าเคสที่หารแล้วได้ทศนิยมแบบ 0.6666666666 ระบบต้องมาหาวิธีปัดครับ ดูตามรูปจาก Wiki ดีกว่าครับ มีอัลกอริทึมที่เกี่ยวข้องด้วย

    ผม Capture รูปมาจาก Wiki ครับ ถ้าสนใจลองตามไปอ่านได้ครับ https://en.wikipedia.org/wiki/Floating_point
    ผม Capture รูปมาจาก Wiki ครับ ถ้าสนใจลองตามไปอ่านได้ครับ https://en.wikipedia.org/wiki/Floating_point

เข้าใจสาเหตุแล้วการแก้ไขหละ

  • Casting มันซะถ้าอยากให้ผลลัพธ์มีทศนิยมได้
  • เปลี่ยน Data Type ของตัวที่เอามาหาร จาก int ไปเป็น Type อื่น ที่สามารถมีทศนิยมได้
    • อย่าลืมว่า ถ้าตัวไตัที่หารกับกลุ่ม Int มี Data Type ที่ใหญ่กว่า Int ได้แก่พวก Double, Decimal เป็นต้น มันมีเรื่อง Numeric Promotion เข้ามาเกี่ยวด้วยนะ อาจจะงงเอาตัวอย่างดีกว่า Decimal / Int = Decimal
  • ใช้ Method มาช่วย ไม่ต้องไปเขียนเองนะครับ MS ทำให้แล้วอย่าง Decimal มี Method Divide มาให้ครับ

จบไปแล้วกับใน C# ผมเข้าใจว่าภาษาอื่นๆ มี Concept คล้ายๆกันครับ ถ้าว่างๆผมไปลองกับฝั่ง Java ให้ครับ และอย่าลืมศึกษาพื้นฐานให้เข้าใจก่อน Coding ครับ ^__^