Category: Developer

งานพัฒนาระบบ, เขียนโปรแกรม

  • Create Excel 2007 file using Interop services.

    Microsoft Office Interop (Excel Automation)คือ ตัวเลือกอีกทางหนึ่งที่ใช้สำหรับ สร้าง อ่าน เอกสาร Excel (XLS, XLSX, CSV) จากการพัฒนาโดยใช้   C# หรือ VB.NET แต่บทความนี้จะขอยกตัวอย่างการพัฒนาเพื่อสร้างเอกสารโดยใช้ C# ค่ะ

    Step 1 

    • เปิด Visual Studio
    • File>New>Project
    • เลือก Visual C#> Windows > Select Console Application

    Step

    • เลือก >> Project Menu >> เลือก Add References >> เลือกที่ Tab COM >> กด “Microsoft Excel 12.0 Object Library”  เพื่อเพิ่ม References
    รูปตัวอย่างการ Add References

    Step

    ตัวอย่าง Code C# เพื่อใช้ในการสร้าง Excel File ดังนี้

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop;
    using Microsoft.Office.Interop.Excel;
     
    namespace ConsoleApplication1
    {
        class Class1
        {
            public static void Main(string[] ar)
            {
                Application ExcelApp = new Application();
                Workbook ExcelWorkBook = null;
                Worksheet ExcelWorkSheet = null;
     
                ExcelApp.Visible = true;
                ExcelWorkBook = ExcelApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
                // ExcelWorkBook.Worksheets.Add(); //Adding New Sheet in Excel Workbook
                try
                {
                    ExcelWorkSheet = ExcelWorkBook.Worksheets[1]; // Compulsory Line in which sheet you want to write data
                    //Writing data into excel of 100 rows with 10 column 
                    for (int r = 1; r < 101; r++) //r stands for ExcelRow and c for ExcelColumn
                    {
                        // Excel row and column start positions for writing Row=1 and Col=1
                        for (int c = 1; c < 11; c++)
                            ExcelWorkSheet.Cells[r, c] = "R" + r + "C" + c;
                    }
                    ExcelWorkBook.Worksheets[1].Name = "MySheet";//Renaming the Sheet1 to MySheet
                    ExcelWorkBook.SaveAs("d:\\Testing.xlsx");
                    ExcelWorkBook.Close();
                    ExcelApp.Quit();
                    Marshal.ReleaseComObject(ExcelWorkSheet);
                    Marshal.ReleaseComObject(ExcelWorkBook);
                    Marshal.ReleaseComObject(ExcelApp);
                }
                catch (Exception exHandle)
                {
                    Console.WriteLine("Exception: " + exHandle.Message);
                    Console.ReadLine();
                }
                finally
                {
     
                    foreach (Process process in Process.GetProcessesByName("Excel"))
                        process.Kill();
                }
             }
        }
    }
    

    เมื่อมีการ Run โปรแกรมแล้ว Excel file ก็จะถูกสร้างตาม path ที่ระบุ ค่ะ

     แหล่งอ้างอิง https://www.c-sharpcorner.com/blogs/creating-excel-file-using-interop-services1

  • TFS : (ตอน) การติดตามการดำเนินโครงการ

    จากบทความ
    การบริหารโครงการโดยใช้เครื่องมือ Team Foundation Server (Phase 4 : การวางแผนงาน)
    และ
    การบริหารโครงการโดยใช้เครื่องมือ Team Foundation Server (Phase 5 : การตรวจสอบงานที่ได้รับมอบหมาย และการบันทึกผลการทำงาน)
    ได้กล่าวถึง ขั้นตอนการใช้ TFS ในการวางแผน การเข้าไปดูงานที่ได้รับมอบหมาย และการบันทึกผลการปฏิบัติงาน

    บทความต่อไปนี้ จะเป็นเนื้อหาต่อเนื่องจาก 2 บทความข้างต้น นั่นคือ เราจะสามารถใช้ TFS ในการติดตามผลการดำเนินงานได้อย่างไรบ้าง
    ในกระบวนการบริหารจัดการโครงการ “ขั้นตอนการติดตามงาน” ถือเป็นขั้นตอนที่สำคัญอย่างมาก และเป็นขั้นตอนที่ผู้จัดการโครงการต้องปฏิบัติ และปฏิบัติอย่างต่อเนื่อง สม่ำเสมอ เพื่อให้สามารถตรวจสอบความล่าช้าของโครงการ และค้นหาปัญหาที่ทำให้ล่าช้า เพื่อนำไปสู่แนวทางแก้ไขที่ทันเวลานั่นเอง

    TFS สามารถตรวจสอบ หรือติดตามงานได้หลายวิธี ในบทความนี้ ได้หยิบยกมาให้เห็นด้วยกัน 3 วิธี อย่าให้เสียเวลา เรามาทำความรู้จัก ในแต่ละวิธีกันเลยค่ะ….

    วิธีที่ 1 ตรวจสอบจาก Stage

    โดย Stage
    TO DO = ยังไม่ดำเนินการ
    IN PROGRESS = อยู่ระหว่างการดำเนินการ
    DONE = ดำเนินการเสร็จแล้ว

    วิธีนี้ เป็นการติดตามงานในภาพของแต่ละงานย่อย หรือ Task นั่นเอง โดยดูจากปริมาณงานที่อยู่ใน Stage ถ้า Tasks ทุก Tasks อยู่ใน Stage = DONE สบายใจได้เลยค่ะ แต่ถ้าจะถึงเวลาปิดโครงการแล้วแต่ Tasks ยังอยู่ใน TO DO หรือ IN PROGRESS จำนวนมาก ต้องรีบสอบถามสมาชิกเลยนะค่ะว่าเกิดอะไรขึ้น เพื่อให้สามารถแก้ไขปัญหาได้ทันท่วงทีค่ะ

    วิธีที่ 2 ตรวจสอบจาก Burndown Chart ซึ่งจะแสดงเป็นกราฟง่ายๆที่ใช้บอกเราว่า “เราทำงานเสร็จไปแล้วเท่าไรและเราเหลืองานที่ต้องทำอีกเท่าไร” แสดงดังรูป

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

    วิธีที่ 3 ตรวจสอบจาก Work By Assinged To ดังรูปด้านล่างค่ะ

    การติดตามจาก Work By Assigned To นั้นดูจำนวนชั่วโมงที่เหลือ ถ้าชั่วโมงยังเหลือเยอะ แสดงว่างานยังเหลืออีกเยอะ ดังนั้นจะต้องไปติดตามรายตัวแล้วค่ะ ว่าเกิดปัญหาอะไรขึ้น วิธีนี้จะดีหน่อยตรงที่ สามารถติดตามในมุมมองของรายบุคคลได้เลยค่ะ

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

    นอกจากนี้ ผู้จัดการโครงการยังสามารถใช้เป็นเครื่องมือในการรายงานความก้าวหน้าของโครงการ เพื่อนำเสนอให้กับผู้บริหาร หรือผู้ที่เกี่ยวข้องได้ทราบถึงสถานะของโครงการ ซึ่งเข้าใจข้อมูลได้ง่าย…(เห็นด้วยใช่ไม๊ค่ะ)

    จากบทความต่าง ๆ ที่เกี่ยวกับ TFS ผู้นำเสนอ หวังเป็นอย่างยิ่งว่าการนำเสนอ หรือบทความแต่ละบทความจะประโยชน์กับทุกๆ ที่สนใจนะค่ะ…. ^___^ อย่างน้อยก็ได้นำเสนอให้ทุกคนได้รู้จักกับเครื่องมือ TFS ในมุมมองของการบริหารจัดการโครงการนะค่ะ…

  • WPF Layout (Part III)

    The WPF Viewbox

    WPF viewbox จะสามารถปรับขนาด/ปรับมิติของ control ของ Windows Presentation Foundation ได้โดยอัตโนมัติ  viewbox และสามารถปรับขนาดของ control เพื่อเติมเต็มพื้นที่ที่มีอยู่ คือจะมีการปรับขนาดของ child ซึ่งเป็น control ที่บรรจุอยู่ใน ViewBox ให้อัตโนมัติ ซึ่งจะมีการปรับพิกัดโดยอัตโนมัติเมื่อมีการปรับเปลี่ยนขนาดเพื่อให้ชุดของพิกัดที่กำหนดเป็นพิกเซลในขนาดดั้งเดิม

    อะไรคือจุดประสงค์ของ WPF Viewbox? เมื่อไหร่ที่คุณจะใช้มัน?

    เมื่อคุณพัฒนาโปรแกรม โดยทั่วไปแล้วคุณจะไม่สามารถรู้ถึงความแน่นอนของ hardware ของระบบที่จะเป็นไป โดยเฉพาะอย่างยิ่งหากแอปพลิเคชันมีไว้เพื่อการใช้งานในระยะยาว คุณไม่สามารถมั่นใจได้เลย   ไม่ว่าจะเป็นขนาดหรือความละเอียดของจอภาพที่แสดงผลการใช้งานของระบบต่างๆ เพราะปีที่แล้วหน้าจออาจจะเป็น ultra-high-resolution ปีต่อมาอาจจะเป็น  low-resolution  monitor  และถ้าคุณได้พัฒนาโปรแกรมโดยคำนึงถึงความละเอียดของหน้าจอของปีที่แล้วไว้ เมื่อมาดูในปีถัดมาในส่วนของหน้าต่างโปรแกรม ปุ่ม และ elements อื่นๆของ GUI ที่ได้พัฒนาไว้ อาจจะมีขนาดที่เล็กมาก จนทำให้เกือบจะใช้ไม่ได้เลย

    Resize!

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

    จากตัวอย่าง XAML code จะสร้าง Viewbox กับ button  ที่เมื่อมีการปรับขนาดของ button  ให้อัตโนมัติเมื่อมีการปรับขนาดของ Viewbox window

     <Window x:Class="ViewboxTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Viewbox Test"
            SizeToContent="WidthAndHeight" Height="75" Width="250">
        <Viewbox>
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>
    </Window>

    What About the Margins?

    จะน่าสนใจขนาดไหนถ้าcontrol ที่อยู่ใน Viewbox  window ก็จะถูกปรับขนาดโดยอัตโนมัติ และจะเกิดอะไรขึ้นเมื่อ window ไม่ได้ถูกปรับขนาดแค่เพียงอย่างเดียวเท่านั้นแต่จะปรับในส่วนของมิติให้กับ control ที่ไม่พอดีกับ window ไปด้วย ?  Viewbox มี 2 properties  คือ Stretch และ StretchDirection ที่จะมาจัดการในส่วนนี้ให้คุณได้

    Stretching it a Bit

    จากโค้ดตัวอย่างข้างต้น เมื่อ complied เป็น   EXE  เราจะเห็นโปรแกรม “Viewbox Test” ที่มีข้อความ “I’m a Button” อยู่บนปุ่ม และเมื่อคุณปรับขนาดของ window ปุ่มก็จะถูกปรับขนาดและอัตราส่วนให้พอดีโดยอัตโนมัติ ทำให้ปุ่มมีขนาดของสัดส่วนที่พอดีสวยงามไม่ว่าจะมีการปรับให้มีขนาดใดก็ตาม ในกรณีที่อัตราส่วนของภาพใน view box แสดงขนาดไม่พอดีกับปุ่ม อาจจะเกิดจากการมีที่ช่องว่างทั้งด้านบนและด้านล่างปุ่มหรือทางซ้ายและขวา

    The Default

    กรณีที่ไม่ได้กำหนดค่าของ Stretch property  จะมีค่า default ของ Viewbox  ที่กำหนดในส่วนของ Stretch property คือ “Uniform” ดังตัวอย่าง

        <Viewbox>
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    จากตัวอย่างข้างต้นจะมีความหมายเหมือนกับตัวอย่างข้างล่าง

        <Viewbox Stretch="Uniform">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    Stretch property จะประกอบด้วย 4 ตัวเลือก  ดังนี้

    1. No Stretch At All

    กรณีที่ไม่ต้องการให้มีการปรับขนาดของ child  control จำเป็นที่จะต้องกำหนดค่า  Stretch property เป็น None ดังตัวอย่าง

       <Viewbox Stretch="None">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    เมื่อมีการกำหนดกำหนดค่า  Stretch property เป็น None ปุ่มจะคงขนาดเดิมไว้ โดยจะไม่เกิดอะไรขึ้นไม่ว่าคุณจะปรับขนาดหรือมิติของ Viewbox window  กรณีถ้ามีการเพิ่มขนาดของ window ปุ่มจะคงขนาดเดิมแต่มีช่องว่างรอบๆปุ่มแทน ส่วนถ้าย่อขนาดของ window ปุ่มก็จะคงขนาดเดิมเช่นกันแต่อาจจะถูก window บังปุ่มจนมองไม่เห็นทั้งปุ่ม หรือสุดท้ายเมื่อย่อขนาดลงเรื่อยๆจนทำให้เรามองไม่เห็นปุ่มเลย

    2.Free-Form

    เมื่อกำหนดค่า Stretch=”Fill”  จะทำให้ child control ถูกปรับขนาดให้พอดีกับ  Viewbox  โดยไม่มีการตัดของเนื้อหาใดๆออกเลย คือจะแสดงผลอย่างสมบูรณ์ ในส่วนของการปรับขนาดในแนวตั้งและแนวนอนอาจจะแตกต่างกัน ซึ่งอาจจะทำให้เนื้อหาใน Viewbox ผิดเพี้ยนไป เนื่องจากจะถูกปรับเปลี่ยนอัตราส่วนเป็นขนาดตาม window ดังตัวอย่าง

        <Viewbox Stretch="Fill">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>
    3.Uniform Adjustment

    Uniform option จะเหมือนกับ Free-Form ที่อธิบายข้างบน และเป็นค่า default ของ Stretch  เนื่องจากตัวลูกของ control จะรักษาขนาดและอัตราส่วนเดิมไว้ มันจะไม่ถูกบิดเบือนขนาดเมื่อมีการปรับขนาด ข้อเสียคือเมื่ออัตราส่วนของ View box มีขนาดไม่ตรงกันกับ control อาจจะทำให้มีช่องว่างรอบๆ control ได้ ดังตัวอย่าง

      <Viewbox Stretch="Uniform">
            <Button Content="I'm a Button" Width="150"/>
      </Viewbox>
    4.Uniform and Fill

    กรณีที่ต้องการใช้ทั้งแบบ Uniform และ Fill เราสามารถกำหนดให้  Stretch=”UniformToFill” ดังตัวอย่าง

        <Viewbox Stretch="UniformToFill">
            <Button Content="I'm a Button" Width="150"/>
        </Viewbox>

    ในส่วน UniformToFill  ที่คล้ายกับ Uniform คือ จะรักษาสัดส่วนต้นแบบของ control ไว้  แต่ในส่วนที่เหมือนกับ Fill คือ UniformToFill จะปรับให้เท่ากับขนาดของ view box  อย่างสมบูรณ์ แต่เนื่องจากจำเป็นต้องรักษาสัดส่วนเดิมไว้และมีการปรับขนาดและมิติตามขนาดของ Viewbox  ไว้ด้วย จะทำให้บางส่วนของ control ถูกตัดออกเพื่อให้มีขนาดพอดีกับสัดส่วนของปลายทาง

    StretchDirection Property ประกอบด้วย 3 ตัวเลือก ดังนี้

    1. UpOnly

    กรณีที่มีการกำหนด StretchDirection=”UpOnly” จะทำให้ ถ้า Viewbox มีขนาดใหญ่กว่า size ของ child control จะส่งผลให้ child control มีขนาดใหญ่ไปด้วย แต่ถ้ามีการลดขนาด child control จะไม่ลดขนาดตาม จึงทำให้การแสดงผลบางส่วนถูกตัดออกไป ดังตัวอย่าง

     <Viewbox Stretch="Uniform" StretchDirection="UpOnly">
            <Button Content="I'm a Button" Width="150"/>
     </Viewbox>
    2. DownOnly

    Viewbox  จะอนุญาตให้ย่อขนาดลงให้น้อยกว่า default ของขนาดเดิม โดยการย่อขนาดลงจะส่งผลให้มีพื้นที่ว่างรอบๆ child control ดังตัวอย่าง

     <Viewbox Stretch="Uniform" StretchDirection="DownOnly">
            <Button Content="I'm a Button" Width="150"/>
     </Viewbox>

    3. Both

    จะเป็นค่า default ของ StretchDirection Property โดย Viewbox จะอนุญาตให้สามารถย่อและขยายขนาดของ child control ได้

     แหล่งอ้างอิง
    https://www.wpftutorial.net

  • Angular : Routing

    ต่อจากการการทำ Directives ในบล็อกก่อนหน้า เราจะมาพูดถึงการสร้าง Routing ที่เป็นส่วนช่วยในการเปลี่ยนหน้าของระบบ ซึ่งจะทำให้ระบบสามารถแบ่งส่วนได้ชัดเจนมากยิ่งขึ้น โดยในหัวข้อนี้เราจะทำการสร้างเมนู เพื่อให้สามารถเปลี่ยนหน้าแบบไม่ต้องโหลดได้ โดยเราจะสร้าง Component “Home” เพื่อเป็นหน้าหลัก โดยใช้คำสั่ง

    >ng generate component home

    จากนั้นให้เราสร้างเมนู ในไฟล์ app.component.html ดังภาพที่ 1

    ภาพที่ 1 สร้างเมนู Home, Calculator, User
    ภาพที่ 2 ผลลัพธ์จากการเพิ่มเมนู

    การที่เราจะทำให้ Angular เปลี่ยนหน้าโดยที่ไม่ต้องโหลดหน้าใหม่ เราสามารถใช้ความสามารถของ Router ที่จะพาไปยังหน้าที่เราต้องการ โดยเราจะต้องเข้าไปทำการตั้งค่าที่ไฟล์ app.module.ts เพื่อให้ Angular ทราบก่อน โดยเราจะนิยาม Route หรือเส้นทางของเราใน myRoutes เพื่อบอกว่าเมื่อไหร่ที่เข้ามาจาก /pages ให้วิ่งไปที่ component ชื่อ HomeComponent, CalculatorComponent หรือ UserComponent และถึงแม้เราจะมีเส้นทางแล้ว แต่หาก root module ของเราไม่รู้จักก็เท่านั้น เราจึงต้องทำให้ root module ของเรารับรู้ผ่าน RouterModule.forRoot() ดังภาพที่ 3

    ภาพที่ 3 การตั้งค่าที่ไฟล์ app.module.ts

    จากนั้นไปยังไฟล์ app.component.html ที่เราสร้างไว้ในภาพที่ 1 และแก้ไข ดังภาพที่ 4 และแสดงผลลัพธ์ในภาพที่ 5

    ภาพที่ 4 ใส่ลิงค์ให้เมนู
    ภาพที่ 5 ผลลัพธ์

    สรุปการทำ Routing เหมาะกับการทำ Single Page Application เพื่อส่ง request ขอข้อมูลแค่บางส่วนของหน้าเท่านั้น…

  • AutoMapper กับขัอมูลชนิด Collection

    การใช้งาน AutoMapper กับ object type array หรือ list ไม่จำเป็นต้องกำหนดค่า config การ map ของ array หรือ list เพียงแค่ config การ map ของ member ของ array หรือ list ที่ใช้งานเท่านั้น ตัวอย่างเช่น object ง่ายๆสองชนิดดังต่อไปนี้

    public class Person
    {
        public string name { get; set; }
    }
    
    public class Employee
    {
       public string name { get; set; }
    }

    กำหนดค่า config ของ AutoMapper สำหรับการ map ข้อมูลจาก Person ไปยัง Employee

    Mapper.Initialize(cfg => cfg.CreateMap<Person, Employee>());
    
    var persons = new[]
    {
       new Person  { Name = "A" },
       new Person  { Name = "B" },
       new Person  { Name = "C" }
    };

    AutoMapper สามารถใช้งานได้กับ generic collection type ดังนี้

    • IEnumerable
    • IEnumerable<T>
    • ICollection
    • ICollection<T>
    • IList
    • IList<T>
    • List<T>
    • Arrays

    ในการทำ mapping ของ AutoMapper ถ้า collection ของ object ปลายทางที่ถูก map มี member อยู่แล้ว AutoMapper จะทำการ remove member ของ collection ปลายทางออกก่อน ที่จะทำการ mapping ข้อมูลจาก Object ต้นทาง

    var enumEmployee = Mapper.Map<Person[], IEnumerable<Employee>>(persons);
    var colEmployee = Mapper.Map<Person[], ICollection<Employee>>(persons);
    var ilistEmployee = Mapper.Map<Person[], IList<Employee>>(persons);
    var listEmployee = Mapper.Map<Person[], List<Employee>>(persons);
    var arrayEmployee = Mapper.Map<Person[], Employee[]>(persons);

    กรณีที่ collection ต้นทางที่ทำการ mapping มีค่าเป็น null, AutoMapper จะทำกำหนดค่าให้กับ collection ปลายทางเป็น empty collection ไม่ใช่ null collection เราสามารถเปลี่ยนค่า default โดยการกำหนดค่า AllowNullCollections = true ในตอน config mapper

    Mapper.Initialize(cfg => {
        cfg.AllowNullCollections = true;
        cfg.CreateMap<Person, Employee>();
    });

    ในกรณีที่ collection ที่ต้องการ mapping มี member ที่มีลักษณะ hierarachy ก็สามารถใช้ AutoMapper ในการ mapping ข้อมูลได้เช่นกัน โดยกำหนด config ของ collection member โดยใช้ method Include หลังจาก config mapping ของ parent object แล้ว และเรียกใช้งาน Map ตามปกติ ดังนี้

    public class ChildPerson : Person 
    {
        public string JobName { get; set; }
    }
    
    public class ChildEmployee : Employee
    {
       public string JobName { get; set; }
    }
    
    
    Mapper.Initialize(c=> 
    {     c.CreateMap<Person, Employee>()        
              .Include<ChildPerson, ChildEmployee>();        
          c.CreateMap<ChildPerson, ChildEmployee>(); 
    }); 
    
    var persons = new[]
    {
       new Person  { Name = "A" },
       new ChildPerson  { Name = "B" },
       new ChildPerson  { Name = "C" }
    }; 
    
    var enumEmployee = Mapper.Map<Person[], IEnumerable<Employee>>(persons);

    อ้างอิง : http://docs.automapper.org/en/stable/Lists-and-arrays.html

  • การเข้ารหัส Password หรือข้อมูลส่วนบุคคลในฐานข้อมูล ด้วย Hash Function กับ Salt Value

    การ Hash
    การ Hash หรือ Hashing ชื่ออย่างเป็นทางการคือ Cryptographic Hash คือการสร้างข้อมูลที่เป็นตัวแทนของข้อมูลที่ต้องการ ซึ่งอาจจะเป็นรหัสผ่าน หรือข้อมูลส่วนบุคคลอื่นๆ และนำไปจัดเก็บในฐานข้อมูลหรือใน Text file หรือในที่อื่นๆ ซึ่งข้อดีของการทำ Hash คือจะไม่สามารถถอดรหัส หรือกระทำการใดๆ เพื่อที่จะ Reverse ให้ออกมาเป็นข้อความต้นฉบับ ซึ่งในปัจจุบันมีวิธีการ Hash มากมาย เช่น MD5, SHA1, SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3 เป็นต้น

    การเขียนโปรแกรม
    ในแง่การเขียนโปรแกรมของแต่ละภาษา จะมี Library หรือเครื่องมือที่เอาไว้ใช้ทำ Hash อยู่แล้ว สามารถเปิดจากคู่มือ ได้เลยครับ

    MD5 Hashing & Cracking
    เป็นการทำ Hash ที่พื้นฐานที่สุด และเมื่อหลายปีที่ผ่านมามีข่าวออกมาว่ามีผู้ Crack ได้สำเร็จ ซึ่งรายละเอียดคร่าวๆ ของเรื่องนี้คือ การทำ Hash ทุกชนิดจะมีการเกิดการซ้ำกันของค่า Hash เนื่องจากการมีคุณสมบัติแทนข้อมูลที่ต้องการ ซึ่งค่า Hash ที่สร้างขึ้นจะมีความยาวที่เท่ากันเสมอ ซึ่งสำหรับ MD5 ก็จะมีความยาว 16 bytes (128 bits) ซึ่งค่า hash ที่เป็นไปได้ทั้งหมดก็จะมีค่า 256^16 (หรือ 2^128) ค่าเท่านั้น ในขณะนี้ที่ข้อมูลที่เราต้องการแทนตัวนั้นอาจเป็นข้อมูลอะไรก็ได้ที่มากกว่าค่า 256^16 (หรือ 2^128) แน่นอน จึงเป็นไปได้ที่จะพบข้อมูลมากกว่า 1 ชุดจะมีค่า Hash ที่ตรงกัน
    ความจริงแล้ว MD5 จะไม่สามารถถอดรหัสได้ เนื่องจาก Hash ทุกชนิดจะผ่านกระบวนการเข้ารหัสแบบทางเดียว ดังนั้นทางที่จะสามารถจะรู้ได้ว่าค่าตั้งต้นของ Hash นี้คืออะไร คือการพยายามสุ่มรหัสที่เป็นไปได้ จากนั้นเอาไปแปลงค่าเป็น MD5 และนำค่าที่ได้ไปเปรียบเทียบ (เรียกว่าเป็นการ Brute force นั่นเอง) ซึ่งถ้าเป็นข้อมูลที่มีความยาวหรือมีความซับซ้อนมาก ก็จะต้องใช้เวลาที่นานขึ้น

    Rainbow Table
    เป็นการเก็บข้อมูล Hash โดยมีข้อมูลต้นฉบับจากการ Brute Force เพื่อความรวดเร็วในการตรวจสอบ ซึ่งในปัจจุบัน GPU ระดับปานกลางหลายๆ รุ่นจะสามารถคำนวน Hash ได้ในระดับ 10 ล้าน Hash ต่อวินาที ซึ่งในปัจจุบันมีผู้ยอมเสียเวลาเพียงครั้งเดียวเพื่อสร้าง Hash ที่มีควายาวมากๆ และมีความซับซ้อน เพื่อในครั้งต่อๆ ไปจะสามารถนำมาาใช้งานได้ทันที และมีให้ดาวน์โหลดได้ฟรีอีกด้วย

    ปัญหาของ Rainbow Table ในปัจจุบันคือ ยังไม่มีการสร้าง rainbow table ขึ้นมาสำหรับ hash ทุกชนิดหรือทุกความยาวของข้อมูลที่ต้องการ ถึงแม้จะมี CPU หรือ GPU ความสามารถสูงๆ แต่การทำ Hash ก็ยังคงใช้พลังในการประมวลผลมากเช่น SHA-2 ขนาด 256 bits ขึ้นไป เป็นต้น

    Image result for hashing with salt

    Salting
    เป็นเทคนิคนึงสำหรับเพิ่มความปลอดภัยสำหรับข้อมูลตั้งต้นของเรา ซึ่งทำให้ใช้เวลาในการถอดรหัสมากขึ้น ดังตัวอย่างเช่น ข้อความที่ต้องการเข้ารหัสตั้งต้นคือ “ThisIsMyPassword” และเมื่อรวมเข้ากับ Salt (ซึ่งอาจมาจากข้อความที่สุ่มขึ้นมา) คือ “3gswgW09seh” จะได้เป็น “ThisIsMyPassword3gswgW09seh” จากนั้นนำข้อความนี้ไป Hasing ซึ่งถ้าคำนวนความน่าจะเป็นของข้อความ กรณีที่เป็นตัวอักษรตัวเล็ก ตัวใหญ่ และตัวเลข มีความเป็นไปได้ 62 แบบ จะเท่ากับว่าถ้ารหัสผ่านที่เราเก็บมีความยาว 16 ตัวอักษร ก็ต้อง Hash ถึง 16^62 แบบ แต่ถ้าเป็นข้อความที่รวมกับSalt แล้วข้างต้น เป็นความยาว 27 ตัวอักษร ผู้ไม่ประสงค์ดีต้อง Hash ถึง 27^62 ถึงจะได้ข้อความที่ถูกต้อง ซึ่งต้องใช้เวลามหาศาลมากกว่าเดิม แต่ข้อเสียของวิธี Salting จะต้องมีการเก็บ Salt Value ในลักษณะของ Plain Text หรือเก็บไว้ในโปรแกรมที่พัฒนา เพื่อการถอดรหัสที่ถูกต้อง และถ้าหาก Salt Value มีการเสียหายหรือเปลี่ยนค่าไปหลังจากการเข้ารหัสเสร็จแล้ว จะไม่สามารถเทียบ Hash เพื่ออ่านข้อความต้นฉบับได้เลย

    Image result for hashing with salt

    Conclusion
    1. การเก็บข้อมูลที่สำคัญ เช่น รหัสผ่าน หรือแม้กระทั่งเลขประจำตัวประชาชน ในฐานข้อมูล ควรเก็บในรูปแบบ Hash เท่านั้น
    2. รหัสผ่านยิ่งยาว ยิ่งใช้เวลาในการถอดรหัสมากขึ้น และยิ่งมีการใช้ Salt Value จะใช้เวลามากยิ่งขึ้น โดยเฉพาะการใช้ Salt Value เพิ่มเข้าไปเพื่อให้มีความยาวมากขึ้น
    3. MD5 ก็ยังเพียงพอต่อการเก็บรหัสผ่านและข้อมูลอื่นๆ แต่ก็ยังสู้ SHA-2 ไม่ได้
    4. ทางที่ดีที่สุดคือ อย่าให้รหัสผ่านหรือข้อมูลที่เก็บอยู่ ถูกเข้าถึงจากภายนอก แม้แต่จะเป็นแค่ Hash ก็ตาม

    Reference:
    https://crackstation.net/hashing-security.htm

  • การเขียน SQL เพื่อเลื่อนลำดับขึ้นลงอัตโนมัติ

    การเขียน SQL เพื่อเลื่อนลำดับขึ้นลงอัตโนมัติของ column ที่ระบุลำดับเป็นตัวเลข

    รูปที่ 1 ตัวอย่างรูปแบบตาราง
    (more…)
  • CrystalReport : ข้อมูลส่วน DetailSection กับการแสดงผลส่วน PageFooterSection

    เนื่องจากผู้เขียนได้รับมอบหมายให้มีการตรวจสอบข้อมูลว่า ถ้ามีข้อมูลใน Field1 ขึ้นต้นด้วยเครื่องหมาย “*” ต้องแสดงข้อความที่ส่วนท้ายของรายงานทุกแผ่นว่า “มีข้อมูลที่มีเครื่องหมาย * นำหน้า”

    ผู้เขียนจึงได้ทำการสร้าง Formula Field ขึ้นมาทั้งหมด 4 อัน คือ

    1. ffCountSubjectStar : ใช้สำหรับเก็บค่าจำนวนข้อมูลใน Field1 ที่ขึ้นต้นด้วย *
    2. ffResetCountStarSubject : ใช้สำหรับ Reset ค่าเมื่อขึ้นหน้าใหม่
    3. ffSumStarSubjectCode : ใช้สำหรับเก็บค่าจำนวนรวมของ Field1 ที่ขึ้นต้นด้วย *
    4. ffPageFooter : ใช้สำหรับเก็บข้อความที่ต้องการให้แสดงท้ายกระดาษ

    1. ffCountSubjectStar

    WhilePrintingRecords;
    Global numberVar
    isStarSubjectCode;

    if (Left({@Field1 },1) = “*”) then
    isStarSubjectCode := 1
    else isStarSubjectCode := 0

    โค้ดส่วนนี้เป็นการตรวจสอบว่าถ้า Field1 นั้นมี * นำหน้า ให้เก็บค่านั้นไว้ในตัวแปร isStarSubjectCode โดย Formula Field นี้จะต้องนำไปใช้ในส่วนของ DetailSection เนื่องจากต้องมีการตรวจสอบข้อมูลทุก Record


    2. ffResetCountStarSubject

    WhilePrintingRecords;

    Global numberVar isStarSubjectCode := 0;
    Global numberVar countStarSubjectCode := 0;

    โค้ดส่วนนี้เป็นส่วนที่ใช้สำหรับ Reset ค่าตัวแปร โดยกำหนดค่าเริ่มต้นให้ตัวแปร ให้เท่ากับ 0 โดย Formula Field นี้จะต้องนำไปใช้ในส่วนของ Page Header เนื่องจากต้องมีการตรวจสอบข้อมูลทุก Record


    3. ffSumStarSubjectCode

    whileprintingrecords; //ทำราย Rec ที่มีการพิมพ์
    Global numberVar countStarSubjectCode;
    Global numberVar isStarSubjectCode;

    countStarSubjectCode := countStarSubjectCode + isStarSubjectCode

    โค้ดส่วนนี้เป็นการนับจำนวน Field1 ที่มี * นำหน้า ให้เก็บผลรวมไว้ในตัวแปร countStarSubjectCode โดย Formula Field นี้จะต้องนำไปใช้ในส่วนของ DetailSection เนื่องจากต้องมีการตรวจสอบข้อมูลทุก Record


    4. ffPageFooter

    ” มีข้อมูลที่มีเครื่องหมาย * นำหน้า”

    เก็บข้อมูลที่ต้องการให้แสดงในส่วนของ PageFooter โดย Formula Field นี้จะต้องนำไปใช้ในส่วนของ PageFooterSection

    ผู้เขียนหวังว่าบทความครั้งนี้จะเป็นประโยชน์ต่อผู้อ่านไม่มากก็น้อย แล้วพบกันใหม่ครั้งหน้าจ้าาาาา ^^

  • AutoMapper

    AutoMapper คือ component ที่ใช้ในการ map ข้อมูลระหว่าง object ต่างชนิดกัน โดยทำการแปลงข้อมูลจาก object ชนิดหนึ่ง ไปกำหนดค่าให้กับ object อีกชนิดหนึ่ง ซึ่งถ้า object ปลายทางตรงตามข้อกำหนดของ AutoMapper ก็ไม่จำเป็นต้อง config ค่าใดๆเลย

    การแปลงข้อมูลจาก object ชนิดหนึ่งไปยัง object อีกชนิดหนึ่ง เกิดขึ้นได้บ่อยในการพัฒนา application โดยเฉพาะ application ที่อยู่ในลักษณะ multi tiers ที่ต้องมีการส่งผ่าน object ระหว่างกัน เช่น UI layer กับ Service layer หรือ Data access layer กับ Service layer

    การใช้งาน AutoMapper

    เมื่อต้องการแปลงข้อมูลจาก object ต้นทางไปยัง object ปลายทางอีกชนิดหนึ่ง ถ้า object ปลายทางมี property name ชื่อเดียวกับ object ต้นทาง AutoMapper จะทำการกำหนดค่าให้กับ property ของ object ปลายทางโดยอัตโนมัติ การกำหนดค่า config การแปลงข้อมูลระหว่าง object ทำได้โดยใช้ MapperConfiguration ซึ่งจะมีเพียงหนึ่งเดียว และสร้างขึ้นตอนเริ่ม application หรือ ใช้ Mapper.Initialize static method

    //static method 
    Mapper.Initialize(cfg => cfg.CreateMap<Person, PersonPoco>());
    
    //instance method
    var config = new MapperConfiguration(cfg => cfg.CreateMap<Person, PersonPoco>());

    type ที่อยู่ทางซ้ายของ cfg.CreateMap<>() คือ type ต้นทาง ส่วนทางด้านขวาคือ type ปลายทาง ซึ่งเราจะทำการ mapping โดยใช้ static Mapper method หรือจะใช้ instance method ก็ได้

    var mapper = config.CreateMapper();
    
    //instance method
    var mapper = new Mapper(config);
    PersonPoco poco = mapper.Map<PersonPoco>(person);
    
    //static method
    PersonPoco poco = Mapper.Map<PersonPoco>(person);

    ในกรณีที่เรามี object ที่มีโครงสร้างซับซ้อน ซึ่งการใช้งานในบางโอกาสที่ต้องการดูข้อมูลเพี่ยงบางส่วน เราสามารถใช้งาน AutoMapper mapping ข้อมูล มาสู่ object ที่มีโครงสร้างเรียบง่ายใช้งานได้สะดวก ตัวอย่างเช่น object Order ที่มีโครงสร้างค่อนข้างซับซ้อนด้านล่างนี้

    public class Order
    {
        public Customer Customer { get; set; }
        public OrderLineItem[] OrderLineItems()
        public decimal GetTotal()
        {
           return OrderLineItems.Sum(li => li.GetTotal());
        }
    }
    
    public class Product
    {
        public decimal Price { get; set; }
        public string Name { get; set; }
    }
    
    public class OrderLineItem
    {
        public Product Product { get; private set; }
        public int Quantity { get; private set;}
        public decimal GetTotal()
        {   
           return Quantity*Product.Price;
        }
    }
    
    public class Customer
    {
         public string Name { get; set; }
    }

    ถ้าต้องการข้อมูลเพียงแค่ CustomerName และ Total เราสามารถนำ AutoMapper มาช่วย Mapping ข้อมูลได้ดังนี้

    public class OrderPoco
    {
        public string CustomerName { get; set; }
        public decimal Total { get; set; }
    }
    
    Mapper.Initialize(cfg => cfg.CreateMap<Order, OrderPoco>()); 
    OrderPoco poco = Mapper.Map<Order, OrderPoco>(order); 
    

    config mapping โดยใช้ static method ทำการ map Order กับ OrderPoco และทำการ mapping โดยเรียกใช้ medthod Map ซึ่งการทำงานเบื้องหลัง AutoMapper จะทำการจับคู่ OrderPoco.Total property กับ Order.GetTotal(), ในส่วนของ OrderPoco.CustomerName จะทำการจับคู่กับ Order.Customer.Name ซึ่งจะเห็นว่าถ้า property name ของ object ปลายทางของการ mapping มีความเหมาะสม ก็ไม่จำเป็นต้องทำการ config mapping สำหรับ property นั้นๆ

    อ้างอิง : http://docs.automapper.org/en/stable/Lists-and-arrays.html