Month: July 2017

  • Oracle: retrieve top n records for each group

    วิธีการเขียน Query เพื่อดึงข้อมูลข้อมูลสูงสุดหรือต่ำสุด N ลำดับแรกของแต่ละกลุ่มออกมาจากตาราง
    สมมติว่าเรามีข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่ซึ่งประกอบด้วย 5 ฟิลด์ข้อมูลดังตัวอย่างข้างล่าง
    ข้อมูล: ตาราง TEST_NEW_STUDENT เป็นตัวอย่างข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการ

    โจทย์: ต้องการดึงข้อมูลนักศึกษาที่ได้คะแนนภาษาอังกฤษสูงสุดแยกตามคณะจากข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการนี้

    เริ่มต้นด้วย query ดังนี้

    SELECT a.*,
    ROW_NUMBER ()
    OVER (PARTITION BY fac_id ORDER BY eng_score DESC)
    AS val_row_number
    FROM test_new_student a

    จุดสำคัญของ query ข้างต้นก็คือฟังก์ชัน ROW_NUMBER ซึ่งเป็นฟังก์ชันที่จะให้เลขบรรทัดของผลลัพธ์ออกมาตามการจัดกลุ่มข้อมูลหรือการเรียงลำดับที่เรากำหนดไว้ด้วยคำสั่ง OVER, PARTITION BY และ ORDER BY ที่ตามมา

    จากตัวอย่างข้างต้นหมายความว่า เรากำลัง Select * จากตาราง TEST_NEW_STUDENT พร้อมกับดึงเลข ROW_NUMBER ออกมา โดยเป็นเลขบรรทัดที่ให้แบ่งกลุ่มด้วยคณะ และให้เรียงลำดับด้วยคะแนนภาษาอังกฤษจากมากไปน้อย ซึ่งผลที่ได้จะเป็นดังนี้

    สังเกตที่ฟิลด์ VAL_ROW_NUMBER จะเห็นว่ามันแสดงตามอันดับของคะแนนภาษาอังกฤษ และถูกแบ่งตามคณะอย่างเรียบร้อย แค่นี้เราก็สามารถที่จะ select เอา Top ที่เท่าไหร่ของแต่ละกลุ่มได้แล้ว โดยเลือกเอา VAL_ROW_NUMBER ที่ต้องการ ซึ่งสามารถทำได้โดยเขียน select…where ซ้อน query ข้างต้นเข้าไปอีกทีดังนี้

    SELECT *
    FROM (SELECT a.*,
    ROW_NUMBER ()
    OVER (PARTITION BY fac_id ORDER BY eng_score DESC)
    AS val_row_number
    FROM test_new_student a)
    WHERE val_row_number <= 1;

    ผลลัพธ์:

    สรุปจากความต้องการที่ตั้งไว้ เราสามารถแก้ได้โดยใช้ Window Function ซึ่งมันสามารถหาเลขลำดับบางอย่างภายในกลุ่มข้อมูลออกมาให้ได้รูปแบบทั่วไปของ query คือ

    1 SELECT *,
    2 WFUNCTION OVER (PARTITION BY GROUP_FIELDS ORDER BY ORDER_FIELDS) n
    3 FROM SOURCE

    โดยที่

    SOURCE คือตัวข้อมูล
    WFUNCTION  หมายถึง Window Function ตัวอย่างที่เราเลือกใช้คือ ROW_NUMBER()
    GROUP_FIELDS คือรายการฟิลด์ที่จะแบ่งกลุ่ม เขียนเหมือนตอนที่เราจะ group by สามารถแบ่งด้วยหลายฟิลด์ก็ได้
    ORDER_FIELDS คือรายการฟิลด์ที่ใช้เรียงลำดับ, สามารถเรียงด้วยหลายฟิลด์ก็ได้, ใส่ ASC หรือ DESC ได้เหมือนคำสั่ง order by ปกติเราไม่จำเป็นต้องใส่ทั้ง partition by และ order by อาจจะใส่แค่อันใดอันหนึ่ง แล้วแต่ว่าต้องการแบ่งกลุ่มหรือเรียงลำดับหรือไม่
    n เป็นชื่อ alias ของผลลัพธ์ จะตั้งชื่ออย่างไรก็ได้ เมื่อเราได้เลขลำดับ n ของแต่ละกลุ่มออกมาแล้ว ทีนี้จะเอาไปหา Top N หรือพลิกแพลงยังไง ก็แล้วแต่จะ query ออกมา

  • Oracle: retrieve top n records from a query

    Top-N queries เป็นวิธีการดึงข้อมูลสูงสุดหรือต่ำสุด N ลำดับแรกออกมาจากตาราง โดยวิธีการดึงข้อมูลแบบ Top-N นั้นมีได้หลายวิธี แต่ในบทความนี้จะนำเสนอวิธีการดึงข้อมูลแบบ Top-N records เพียง 3 วิธีการดังนี้

    1. Inline View and ROWNUM
    2. WITH Clause and ROWNUM
    3. ROW_NUMBER

     

    สมมติว่าเรามีข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่ซึ่งประกอบด้วย 5 ฟิลด์ข้อมูลดังตัวอย่างข้างล่าง
    ข้อมูล: ตาราง TEST_NEW_STUDENT เป็นตัวอย่างข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการ

    โจทย์: ต้องการดึงข้อมูลนักศึกษาที่ได้คะแนนภาษาอังกฤษสูงสุด 5 อันดันแรกจากข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการนี้

     

    เริ่มต้น Top-N query ตามลำดับเพื่อแก้โจทย์กันค่ะ

    Inline View and ROWNUM
    Classic Top-N style query

    SELECT a.*,rownum
    FROM (SELECT *
    FROM test_new_student
    ORDER BY eng_score desc) a
    WHERE ROWNUM <= 5;

    ผลลัพธ์:

    • จากผลลัพธ์ที่ได้ข้อมูลจะถูกจัดเรียงจากคะแนนจากมากไปน้อยก่อนด้วย ORDER BY clause และหลังจากนั้นก็จะจำกัดจำนวนข้อมูลที่ต้องการด้วย ROWNUM
    • Pseudocolumn ROWNUM เป็นค่าตัวเลขแสดงลำดับที่ของการดึงข้อมูลจากตาราง
    • กรณีที่ต้องการข้อมูลคะแนนภาษาอังกฤษต่ำสุด ใส่ ASC แทน DESC ตรง ORDER BY clause

     

    WITH Clause and ROWNUM
    จากตัวอย่างข้างต้นเรายังสามารถเขียน query ด้วย WITH clause แทนที่ inline view ได้ดังนี้

    WITH ordered_query AS
    (SELECT *
    FROM test_new_student
    ORDER BY eng_score desc)
    SELECT ordered_query.*,rownum
    FROM ordered_query
    WHERE rownum <= 5;

     

    ROW_NUMBER
    ฟังก์ชัน ROW_NUMBER เป็นฟังก์ชันที่กำหนดค่าลำดับของข้อมูลที่จัดเรียงตามข้อมูลที่กำหนดไว้ใน order_by_clause โดยจะมีค่าเริ่มต้นเท่ากับ 1 โดยเราจะมีวิธีการเขียน query ได้ดังนี้

    SELECT *
    FROM (SELECT a.*, row_number() OVER (ORDER BY eng_score DESC) AS val_row_number
    FROM test_new_student a)
    WHERE val_row_number <= 5;

    ผลลัพธ์:

    สังเกตที่ฟิลด์ VAL_ROW_NUMBER จะเห็นว่ามันแสดงตามอันดับของคะแนนภาษาอังกฤษแล้ว แค่นี้เราก็สามารถที่จะ select เอา Top ที่เท่าไหร่ได้แล้ว โดยเลือกเอา VAL_ROW_NUMBER ที่ต้องการ

  • เปลี่ยน ubuntu sources.list ก่อนสร้าง image ด้วย dockerfile

    การใช้งาน docker นั้นเราสามารถใช้ image จาก docker hub หรือเราจะสร้าง image ของเราเอง ซึ่งมีหลายวิธีในการสร้าง image แบบของเราเอง (custom) วิธีหนึ่งคือการใช้ dockerfile อย่างคร่าว ๆ คือ

    mkdir ~/mydocker
    cd ~/mydocker
    touch dockerfile
    docker built -t test_app:20170713 .
    docker images

    ในไฟล์ชื่อ dockerfile นี้จะมีไวยกรณ์ประมาณนี้

    # Image tag: test_app:20170713 <– บรรทัดนี้คือ comment
    FROM ubuntu:16.04 <– บรรทัดนี้คือ ไปเอา image ชื่อ ubuntu:16.04 จาก docker hub
    RUN apt-get update <– บรรทัดนี้คือ คำสั่งบอกว่าจะติดตั้ง หลังคำว่า RUN นั่นเอง
    RUN apt-get dist-upgrade -y
    RUN apt-get install -y apache2 libapache2-mod-php7.0 php7.0
    COPY …
    ADD …
    EXPOSE …
    CMD …
    และยังมี command อื่น ๆ อีก

    ทีนี้จากการที่ต้องลองผิดลองถูกบ่อย ๆ จึงพบว่า หากเราเพิ่มคำสั่ง 2 บรรทัดนี้เข้าไปก่อนบรรทัด RUN apt-get update ก็จะทำให้เราได้ใช้ ubuntu repository ที่ต้องการแทนค่า default ที่ archive.ubuntu.com เช่นต้องการให้มาใช้ th.archive.ubuntu.com ก็เขียนคำสั่งดังนี้

    RUN sed -i 's/\/us.archive/\/th.archive/g' /etc/apt/sources.list
    RUN sed -i 's/\/archive/\/th.archive/g' /etc/apt/sources.list

    ผลลัพธ์คือ หลังจากทำคำสั่ง docker built -t test_app:20170713 . จะเห็นว่าในขั้นตอนการ build นั้นจะดาวน์โหลดไฟล์ได้รวดเร็วกว่าเดิมมาก

    จึงนำความรู้มาบอกกันครับ อ้อลืมบอกว่าบทความที่เขียนนี้ ผมทดสอบกับ docker version 17.06.0-ce ครับ

    อยากแนะนำความรู้เกี่ยวกับ docker ที่อ่านมา พบว่าน่าสนใจ ลองอ่านดูครับ อ่านง่าย

     

    บทความในต่างประเทศ

    1. How To Install and Use Docker on Ubuntu 16.04 (https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04)
    2. How to Build an Image with the Dockerfile (https://www.sitepoint.com/how-to-build-an-image-with-the-dockerfile/)
    3. Dockerfile reference (https://docs.docker.com/engine/reference/builder/)
    4. Best practices for writing Dockerfiles (https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)
    5. How to update Docker image to maintain your containers secure  (https://bobcares.com/blog/update-docker-image/2/)
    6. How to upgrade docker container after its image changed
      (https://stackoverflow.com/questions/26734402/how-to-upgrade-docker-container-after-its-image-changed)
    7. Manage data in containers (https://docs.docker.com/engine/tutorials/dockervolumes/)

     

  • รู้จักฟังก์ชัน Excel ตอนที่ 2 เรื่อง ตระกูลท่าน Count (COUNTIF, COUNTIFS)

    พบกันอีกครั้งนะคะ กับ Excel ตอน ตระกูลท่าน Count ตอนที่ 2 ค่ะ สำหรับตอนที่ 2 นี้จะเป็นการแนะนำการใช้ Function COUNTIF และ COUNTIFS มาเริ่มกันเลยค่ะ

    COUNTIF

    เป็น Function ที่ใช้สำหรับนับข้อมูลตามเงื่อนไขที่เราต้องการ 1 เงื่อนไข เช่น

    • ต้องการนับจำนวนคนที่ได้เกรด A : เงื่อนไขคือ “คนที่ได้เกรด A”
    • ต้องการนับจำนวนคนที่ได้คะแนนมากกว่า 75 : เงื่อนไขคือ “คะแนนมากกว่า 75”

    รูปแบบ Function คือ COUNTIF(range, criteria)

    • range คือ ช่วงข้อมูลที่ต้องการนับจำนวน
    • criteria คือ เงื่อนไขที่ต้องการ

    ตัวอย่าง

    จากตัวอย่าง สิ่งที่ต้องการคือ หาจำนวนคนที่ได้เกรด A ดังนั้น

    • range คือ ช่วง D3 ถึง D7
    • criteria คือ “A”

    ดังนั้นสิ่งที่จะได้เมื่อเขียน Function คือ =COUNTIF(D3:D7,”A”) ผลลัพธ์ที่ได้คือ 2 ดังภาพ

     


    COUNTIFS

    เป็น Function ที่ใช้สำหรับนับข้อมูลตามเงื่อนไขที่เราต้องการโดยเงื่อนไขนั้นมีมากกว่า 1 เงื่อนไข เช่น

    • ต้องการนับจำนวนคนที่ได้เกรด A และเป็นเพศหญิง : เงื่อนไขคือ “คนที่ได้เกรด A” และ “เพศหญิง”
    • ต้องการนับจำนวนเด็กเข้าอบรมได้คะแนนมากกว่า 60 แต่น้อยกว่า 79 : เงื่อนไขคือ “คะแนนมากกว่า 60 แต่น้อยกว่า 79”

    รูปแบบ Function คือ COUNTIF(range_criteria1, criteria1, [range_criteria2, criteria2],…)

    • range_criteria1 คือ ช่วงข้อมูลที่ต้องการนับจำนวนของเงื่อนไขที่ 1 ค่านี้จำเป็นต้องระบุ
    • criteria1 คือ เงื่อนไขที่ต้องการ เงื่อนไขแรก ค่านี้จำเป็นต้องระบุ
    • range_criteria2 คือ ช่วงข้อมูลที่ต้องการนับจำนวนของเงื่อนไขที่ 2 ค่านี้ไม่จำเป็นต้องระบุ
    • criteria2 คือ เงื่อนไขที่ต้องการ เงื่อนไขที่สอง ค่านี้ไม่จำเป็นต้องระบุ
    • สามารถระบุเงื่อนไขที่ต้องการได้เรื่อย ๆ

    ตัวอย่าง

    จากตัวอย่าง สิ่งที่ต้องการคือ หาจำนวนคนที่ได้เกรด A และได้คะแนนมากกว่า 90 ดังนั้น

    เงื่อนไขแรก

    • range_criteria1 คือ ช่วง D3 ถึง D7
    • criteria1 คือ “A”

    เงื่อนไขที่สอง

    • range_criteria2 คือ ช่วง E3 ถึง E7
    • criteria2 คือ “>=90”

    ดังนั้นสิ่งที่จะได้เมื่อเขียน Function คือ =COUNTIFS(D3:D7,”A”,E3:E7,”>=90″) ผลลัพธ์ที่ได้คือ 1 ดังภาพ

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

    แล้วพบกันใหม่ตอนหน้าค่ะ 

  • รู้หรือไม่ : บริการโทรศัพท์ภายในมหาวิทยาลัยสงขลานครินทร์

    เกี่ยวกับ บริการโทรศัพท์ภายในมหาวิทยาลัยสงขลานครินทร์ เผื่อท่านใดไม่ทราบ
    (เป็นการสรุปจาก “คู่มือการใช้งานโทรศัพท์พื้นฐาน”  http://telecom.cc.psu.ac.th/telephone/fn.pdf )
    — ที่สรุปไว้นี่เพื่อให้ง่ายต่อการใช้งานอย่างรวดเร็วมากยิ่งขึ้นเท่านั้น และเป็นเฉพาะที่ใช้บ่อยทำนั้น —

    1. ติดต่อ Operator : กด 9
    2. โทรซ้ำเบอร์ที่เพิ่มโทรไป : กด *70
    3. รับสายแทนอีกเครื่องนึงที่ดัง : กด *72 ตามด้วยหมายเลขที่ดัง
    4. ฝากสายให้อีกเบอร์รับ ทันที : กด *60 ตามด้วยหมายเลขที่จะช่วยรับสายแทน
    5. ฝากสายให้อีกเบอร์รับ กรณีสายไม่ว่าง : กด *61 ตามด้วยหมายเลขที่จะช่วยรับสายแทน
    6. ฝากสายให้อีกเบอร์รับ กรณีไม่มีคนรับสาย : กด *62 ตามด้วยหมายเลขที่จะช่วยรับสายแทน
    7. ฝากสายให้อีกเบอร์รับ กรณีทั้ง สายไม่ว่าง และ ไม่มีคนรับสาย : กด *63 ตามด้วยหมายเลขที่จะช่วยรับสายแทน
    8. ยกเลิกการฝากสาย: กด *64
    9. โทรกลับเบอร์ที่โทรเข้ามาล่าสุด: กด *68
  • รู้จักฟังก์ชัน Excel ตอนที่ 1 เรื่อง ตระกูลท่าน Count

    หลาย ๆ ท่านคงใช้ Excel อยู่ในชีวิตประจำวันไม่มากก็น้อย อาจจะชินตากับ Function Count กันอยู่บ่อย ๆ แต่ Function นี้ ไม่ได้มาเดี่ยว ๆ นะคะ ยังมีญาติ ๆ ในตระกูลอีกเพียบเลย มาดูกันค่ะว่า มีอะไรบ้าง และแต่ละ Function นั้นทำงานกันอย่างไรค่ะ

    1. COUNT
    2. COUNTA
    3. COUNTBLANK
    4. COUNTIF
    5. COUNTIFS

    Function ตระกูล Count หลัก ๆ ที่ผู้เขียนใช้งานจะมี 5 Function ข้างต้นนะคะ สำหรับในตอนที่ 1 นี้ ผู้เขียนจะนำเสนอ 3 ฟังก์ชันแรกก่อนก็คือ COUNT, COUNTA และ COUNTBLANK ค่ะ ส่วนอีก 2 Function สามารถติดตามต่อได้ในตอนที่ 2 นะคะ

     

    COUNT 

    สำหรับฟังก์ชันนี้ จะใช้สำหรับนับจำนวนเฉพาะตัวเลข โดยไม่นับตัวอักษรและช่องว่าง ที่อยู่ในช่วงที่เรากำหนด(range) หรือเลือกทีละค่า(value) ตามที่เราต้องการ

    รูปแบบ Function แบบ value คือ COUNT(value1, [value2],…)

    • value1 คือ ค่าข้อมูลแรกที่ต้องการนับจำนวน ในที่นี้คือใส่ทีละค่า ค่านี้จำเป็นต้องระบุ
    • value2 คือ ค่าข้อมูลที่สอง ที่ต้องการนับจำนวน ไม่จำเป็นต้องมี
    • สามารถเลือกค่าได้เรื่อย ๆ

    ตัวอย่าง

    ภาพที่ 1 การเลือกทีละค่าเพื่อนับจำนวนโดยใช้ Function Count

     

    รูปแบบ Function แบบ range คือ COUNT(value1, [value2],…)

    • value1 คือ ค่าข้อมูลแรกที่ต้องการนับจำนวนในที่นี้คือใส่เป็นช่วง การระบุคือ จุดเริ่มต้น:จุดสิ้นสุด ค่านี้จำเป็นต้องระบุ
    • value2 คือ ค่าข้อมูลที่สอง ที่ต้องการนับจำนวน ไม่จำเป็นต้องระบุ
    • สามารถเลือกค่าได้เรื่อย ๆ

    ตัวอย่าง

    ภาพที่ 2 การเลือกค่าเป็นช่วงเพื่อนับจำนวนโดยใช้ Function Count

    ผลลัพธ์ที่ได้

    หมายเหตุ เนื่องจาก จากภาพที่ 1 และ 2 มีการเลือกค่าเท่ากับการเลือกแบบช่วงดังนั้นค่าที่ได้จะเท่ากันค่ะ

    จากผลลัพธ์ที่ได้ จะเห็นได้ว่า Function Count จะนับเฉพาะตัวเลขเท่านั้น ส่วนตัวอักษรหรือช่องว่าง จะไม่ถูกนับค่ะ

    ซึ่งจะเห็นได้ว่า ข้อมูลคำนำหน้า เกรดและคะแนน จะนับได้แค่ 0 เนื่องจากมีช่องว่างข้อมูลและข้อมูลใน Column นั้นเป็นตัวอักษรค่ะ


    COUNTA

    สำหรับฟังก์ชันนี้ จะใช้สำหรับนับจำนวนข้อมูลทั้งหมดทั้งตัวอักษรและตัวเลข แต่ไม่นับช่องว่าง ที่อยู่ในช่วงที่เรากำหนด(range) หรือเลือกทีละค่า(value) ตามที่เราต้องการดังภาพค่ะ

    รูปแบบ Function แบบ value คือ COUNTA(value1, [value2],…)

    • value1 คือ ค่าข้อมูลแรกที่ต้องการนับจำนวน จะระบุทีละค่าหรือระบุเป็นช่วงก็ได้ แต่ค่านี้จำเป็นต้องระบุ
    • value2 คือ ค่าข้อมูลที่สอง ที่ต้องการนับจำนวน ไม่จำเป็นต้องมี
    • สามารถเลือกค่าได้เรื่อย ๆ

    ตัวอย่าง

    จากผลลัพธ์ที่ได้ จะเห็นได้ว่า Function CountA จะนับข้อมูลทั้งหมด ยกเว้นช่องว่าง จะไม่ถูกนับค่ะ

    ซึ่งจะเห็นได้ว่า ข้อมูลเกรดและคะแนน จะนับได้แค่ 4 เนื่องจากมีช่องว่างข้อมูลละช่อง


    COUNTBLANK

    สำหรับฟังก์ชันนี้ จะใช้สำหรับนับจำนวนเฉพาะช่องว่าง ที่อยู่ในช่วงที่เรากำหนด(range)

    รูปแบบ Function แบบ value คือ COUNTBLANK(range)

    • range คือ ช่วงของข้อมูลที่ต้องการ

    ตัวอย่าง

    จากผลลัพธ์ที่ได้ จะเห็นได้ว่า Function CountBlank จะนับเฉพาะข้อมูลที่เป็นช่องว่างค่ะ

    ซึ่งจะเห็นว่าข้อมูลในที่นี้มีช่องเกรดและช่องคะแนน ที่มีช่องว่าง Column ละช่อง

     

    สำหรับในตอนที่ 1 ก็ขอจบลงเพียงเท่านี้ สามารถติดตาม Function COUNTIF และ COUNTIFS ต่อได้ในตอนที่ 2 ค่ะ 

  • Migration project.json to csproj format (C#)

    ในช่วงการพัฒนาของ .NET Core tooling จนถึงปัจจุบัน มี design/component หลายอย่างที่มีการเปลี่ยนแปลงในลักษณะที่ไม่ compatible กับ version ก่อนหน้า หรือยกเลิกการใช้งาน หนึ่งในนั้นก็คือ project config file ที่เริ่มต้นใช้รูปแบบ json ซึ่งอยู่ใน file ที่ชื่อ project.json แต่ปัจจุบัน เปลี่ยนมาใช้ MSBuild/csproj format

    การ migration project.json ไปสู่ .csproj format ทำได้ด้วยกันสองวิธีคือ

    • Visual Studio 2017
    • dotnet migrate command-line tool

    ทั้งสองวิธีใช้กลไกการทำงานเดียวกัน ซึ่งผลที่ได้จะเหมือนกัน

    Visual Studio 2017

    เปิด project โดยเปิด file .xproj ใน Visual Studio 2017 จะปรากฎ One-way upgrade dialog ขึ้นมาให้เลือก OK, Visual Studio จะทำการ migrate โดย file ที่ถูก migrate (project.json, global.json, .xproj) จะถูกย้ายไปสำรองไว้ใน folder Backup

    dotnet migrate

    ใช้ command-line เข้าไปที่ folder ที่เก็บ project และใช้คำสั่ง dotnet migrate ซึ่งจะทำการ migrate โดย file ที่ถูก migrate (project.json, global.json, .xproj) จะถูกย้ายไปสำรองไว้ใน folder Backup

    <Project Sdk="Microsoft.NET.Sdk">
      ... 
    </Project>
    

    ข้อแตกต่างระหว่าง project.json กับ csproj format ( อยู่ในรูปแบบ XML-based ซึ่งมี root node ระบุ sdk ที่ใช้คือ Microsoft.NET.Sdk สำหรับ web project sdk ที่ใช้คือ Microsoft.NET.Sdk.Web ) มีดังนี้

    Common options

    ****** JSON format ******
    {
      "name": "MyProjectName",
      "version": "1.0.0-alpha-*",
    
      "authors": [ "name1", "name2" ],
      "company": "PSU",
      "language": "en-US",
      "title": "My library",
      "description": "This is my library.",
      "copyright": "PSU 3000",
      "userSecretsId": "xyz123"
    }
    
    ****** csproj format ******
    <PropertyGroup>
     <AssemblyName>MyProjectName</AssemblyName>
     <PackageId>MyProjectName</PackageId>
     <VersionPrefix>1.0.0</VersionPrefix>
     <VersionSuffix>alpha</VersionSuffix>
    
     <Authors>name1;name2</Authors>
     <Company>PSU</Company>
     <NeutralLanguage>en-US</NeutralLanguage>
     <AssemblyTitle>My library</AssemblyTitle>
     <Description>This is my library.</Description>
     <Copyright>PSU 3000</Copyright>
     <UserSecretsId>xyz123</UserSecretsId>
    </PropertyGroup>
    

    frameworks

    ****** JSON format ******
    {
      "frameworks": {
        "netcoreapp1.0": {},
        "net451": {}
      }
    }
    
    ****** csproj format ******
    <PropertyGroup>
     <TargetFrameworks>netcoreapp1.0;net451</TargetFrameworks>
    </PropertyGroup>
    

    dependencies

    ****** JSON format ******
    {
      "dependencies": {
        "NETStandard.Library": "1.6.0",
        "Microsoft.AspNetCore": "1.1.0",
    
        "MyOtherProject": "1.0.0-*",
        "AnotherProject": {
          "type": "project"
        }
      }
    }
    
    ****** csproj format ******
    <PropertyGroup>
     <NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion> 
    </PropertyGroup>
    
    <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore" Version="1.1.0" /> 
    </ItemGroup>
    
    <ItemGroup>
     <ProjectReference Include="..\MyOtherProject\MyOtherProject.csproj" />
     <ProjectReference Include="..\AnotherProject\AnotherProject.csproj" /> 
    </ItemGroup>
    

    tools

    ****** JSON format ******
    {
      "tools": {
        "Microsoft.EntityFrameworkCore.Tools.DotNet": "1.0.0-*"
      }
    }
    
    ****** csproj format ******
    <ItemGroup>
     <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> 
    </ItemGroup>
    

    buildOptions

    ****** JSON format ******
    {
      "buildOptions": {
        "emitEntryPoint": true
        "warningsAsErrors": true,
        "nowarn": ["CS0168", "CS0219"],
        "xmlDoc": true,
        "preserveCompilationContext": true,
        "outputName": "Different.AssemblyName",
        "debugType": "portable",
        "allowUnsafe": true,
        "define": ["TEST", "OTHERCONDITION"]
      }
    }
    
    ****** csproj format ******
    <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <NoWarn>$(NoWarn);CS0168;CS0219</NoWarn>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>     
     <PreserveCompilationContext>true</PreserveCompilationContext> 
     <AssemblyName>Different.AssemblyName</AssemblyName>
     <DebugType>portable</DebugType> 
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> 
     <DefineConstants>$(DefineConstants);TEST;OTHERCONDITION</DefineConstants>
    </PropertyGroup>
    

    testRunner

    ****** JSON format ******
    {
      "testRunner": "xunit",
      "dependencies": {
        "dotnet-test-xunit": ""
      }
    }
    
    ****** csproj format ******
    <ItemGroup>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-*" />
     <PackageReference Include="xunit" Version="2.2.0-*" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-*" /> 
    </ItemGroup>
    

    อ้างอิง : https://docs.microsoft.com/en-us/dotnet/core/tools/project-json-to-csproj

  • ASP.NET API Security

    ปัจจุบันการพัฒนาโปรแกรมในรูปแบบของ API นั้นแพร่หลายมาก เนื่องจากจะทำให้โปรแกรมยืดหยุ่น สามารถพัฒนา Interface ไปในรูปแบบที่หลากหลาย ทั้ง Desktop, Mobile โดยเฉพาะการเรียก API ผ่าน http นั้น ถือว่าค่อนข้างที่จะยืดหยุ่นกับเกือบจะทุก platform ดังนั้น การรักษาความปลอดภัยให้กับ API เหล่านี้ เป็นสิ่งที่จำเป็นและสำคัญอย่างยิ่ง

    บทความนี้จะพูดถึง 2 เรื่องหลักๆ ได้แก่ การ Authentication และการ Authorization ดังนี้ครับ

     

    Authentication

    เปรียบเสมือนกับการตรวจสอบว่าใครเป็นผู้ร้องขอ (request) ซึ่งอาจจะเป็นในลักษณะของ username/password หรือเป็น API Key จากใน HTTP Request Header

    ในบทความนี้จะขอข้ามการพูดถึงการ authentication ด้วย username/password เพราะเชื่อว่าสามารถทำกันได้อยู่แล้ว ไม่ว่าจะเป็นการเขียน provider เองหรือใช้ provider ที่มีมาให้กับ .net framework ซึ่งได้แก่ MembershipProvider โดยจะขอเริ่มพูดในส่วนของ API Key ซึ่งจะใช้คลาส HttpMessageHandler (ทำงานใน http message level ดีกว่าไปทำใน controller แน่นอนครับ) วิธี implement คือ การสร้าง Class ที่ inherite มาจาก DelegatingHandler (ซึ่งมาจาก HttpMessageHandler อีกที) จะให้ override ส่วนของการตรวจสอบ HTTP Request โดยการทำ overriding method ชื่อ SendAsync และเพื่อให้ทำงานได้ จะต้องทำการ register handler ที่ Global.asax ใน Application_Start ด้วยครับ ด้วยคำสั่ง GlobalConfiguration.Configuration.MessageHandlers.Add(new MY_CLASS());

    API Key Authentication

    เราจะต้องมี API Key โดยสามารถเก็บไว้เป็นค่าคงที่ หรือเก็บไว้เป็นข้อมูลในฐานข้อมูล จากนั้นทำการตรวจสอบ Request ที่เข้ามาด้วยคำสั่งต่อไปนี้

    HttpRequestMessage.Headers.TryGetValues(“API_KEY”, out myHeader)
    (ต้องทำการสร้าง instance ของ HttpRequestMessage ก่อนนะครับ — myHeader เป็น type IEnumerable<string>)

    จากนั้นเรานำค่าในตัวแปรมาตรวจสอบกับ API Key ของเรา ที่เราเก็บไว้ เช่น

    ถ้าเก็บไว้เป็นค่าคงที่ ก็ตรวจสอบดังนี้

    myHeader.FirstOrDefault().Equals(“MySecretAPIKeyNaJa”);

    หรือถ้าเก็บไว้ในฐานข้อมูล ก็ตรวจสอบดังนี้

    db.API_KEY.Where(w => w.KEY == myHeader.FirstOrDefault()).Count() > 0 เป็นต้น

    หากเป็น API Key ที่ถูกต้อง สามารถ return response ดังนี้ได้ทันที await base.SendAsync(HttpRequestMessage, CancellationToken);
    ส่วนถ้าเป็น API Key ที่ไม่ถูกต้อง สามารถ return response ดังนี้ เพื่อให้ browser รู้ว่าเกิดอะไรขึ้น HttpRequestMessage.CreateResponse(HttpStatusCode.Forbidden, “Invalid API Key”);

     

    Authorization

    การทำ authorization นี้จะใช้งาน RoleProvider จาก .NET Framework ซึ่งจะต้องทำการ Implement role provider มาก่อน (รายละเอียด: https://msdn.microsoft.com/en-us/library/8fw7xh74.aspx)

    หลังจากการทำยืนยันตัวตน (Authentication) แล้ว ควรจะทำการตรวจสอบการอนุญาตให้เข้าถึงทรัพยากรหรือการกระทำ (action) ด้วย ด้วยการใช้งาน AuthorizeAttribute ซึ่งเป็น filter attribute ด้วยวิธีการง่ายๆ เพียงการใส่ [Authorize] ไว้บน Controller (Class) หรือ Action (Method) ซึ่งจะมี attribute [AllowAnonymous] ให้สามารถใช้ได้ สำหรับ controller, action ที่ไม่ต้องการการ authentication หรือ authorization ครับ

     

    นอกจากนี้เรายังสามารถระบุ role กับ attribute ได้อีกด้วย เช่น [Authorize{Roles = “Manager, ValidUsers”}] เป็นต้นครับ

     

     

     

  • การพัฒนา Unit Testing โดย MSTest test library (.NET Core)

    บทความนี้นำเสนอขั้นตอนการพัฒนา Unit Test อย่างง่ายโดยแสดงให่้เห็นการพัฒนาทีละขั้นตอน สำหรับระบบที่พัฒนาบน .NET Core โดยใช้ MSTest เพื่อทำความเข้าใจการพัฒนาแบบ test-driven development (TDD) ซึ่งในขั้นแรก เริ่มต้นด้วยการสร้าง source project ชื่อ “CalcTool” ใช้สำหรับการทดสอบ อยู่ภายใต้ folder “TestSample” ซึ่งขั้นตอนการสร้าง project สามารถดูได้จาก บทความ การพัฒนา Unit Testing โดย xUnit test library (.NET Core) ซึ่งจะได้ class “Calc” ซึ่งประกอบไปด้วย method “AbsAddByOne(int value)” ที่ยังไม่ได้เขียน code การทำงาน

    สร้าง test project ที่ใช้ MSTest library โดยเปิด command prompt เข้าไปที่ folder “TestSample” และสร้าง sub folder ชื่อ “CalcTool.MSTests” จากนั้นเข้าไปที่ folder “CalcTool.MSTests” ทำการสร้าง .NET Core project โดยใช้คำสั่ง

    dotnet new mstest

    โดยคำสั่งนี้จะทำการสร้าง test project ที่ใช้ MSTest test library และกำหนดค่า test runner

    <ItemGroup>
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0"/>
        <PackageReference Include="MSTest.TestAdapter" Version="1.1.11"/>
        <PackageReference Include="MSTest.TestFramework" Version="1.1.11"/>
    </ItemGroup>
    

    เพิ่ม reference ไปยัง project ที่ต้องการทดสอบซึ่งในที่นี้คือ CalcTool project โดยใช้คำสั่ง

    dotnet add reference ../CalcTool/CalcTool.csproj

    จากนั้นให้ execute คำสั่ง dotnet restore เพื่อ restore NuGet package ที่จำเป็นต้องใช้ในแต่ละ project

    เริ่มพัฒนา unit testing โดยลบ file “UnitTest1.cs” และสร้าง file ใหม่โดยใช้ชื่อว่า “CalcTest.cs” และเขียน code ดังนี้

    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace CalcTool.MSTest
    {
        [TestClass]
        public class CalcTest
        {
            [TestMethod]
            public void AbsAddByOneTest()
            {
                var c = new Calc();
                var result = c.AbsAddByOne(5);
    
                Assert.AreEqual(result, 6);
            }
        }
    }
    

    *[TestClass] attribute ใช้เพื่อบอกว่ามี unit test อยู่ใน class นั้น
    *[TestMethod] attribute ใช้เพื่อกำหนดว่า method นั้นๆเป็นแบบ single test

    ทำการทดสอบโดยการ execute dotnet test ซึ่งจะทำการ build และ start MSTest test runner ซึ่งพบว่าผลการทดสอบ fail เนื่องจากยังไม่ได้ implement code ใน method “AbsAddByOne” ของ class “Calc” ดังนั้นกลับไปที่ method “AbsAddByOne” เพิ่ม code ลงไปเพื่อให้ผลการทดสอบถูกต้อง/ผ่าน (test pass)

            public int AbsAddByOne(int value)
            {
                return Math.Abs(value) + 1;
                //throw new NotImplementedException("Not implement");
            }
    

    กลับไปที่ folder “CalcTool.MSTests” และ execute dotnet test ซึ่งจะทำการ build และ start MSTest test runner ซึ่งพบว่า ผ่านการทดสอบ (test pass)

    MSTest มี attribute ที่ใช้กำหนด suite of tests ซึ่ง execute code เดียวกันแต่มี input ที่มีค่าแตกต่างกันนั่นคือ [DataTestMethod] attribute และใช้ [DataRow] attribute ในการกำหนดค่าของ input

            [DataTestMethod]
            [DataRow(5)]
            [DataRow(-5)]
            public void AbsAddByOneTest2(int value)
            {
                var c = new Calc();
                var result = c.AbsAddByOne(value);
    
                Assert.AreEqual(result, 6);
            }
    

    การพัฒนาในรูปแบบ test-driven development (TDD) จะทำเป็นรอบๆในลักษณะนี้โดย เพิ่ม unit testing , เพิ่ม code ใหม่เข้าไป ทดสอบ unit test จนผ่านและเริ่มรอบใหม่ จนกระทั่งได้ test ชุดสุดท้ายและโปรแกรม/library ที่สมบูรณ์ ก็เป็นอันจบการพัฒนาในรูปแบบ test-driven development (TDD)