Tag: asp .net

  • Refresh ข้อมูลในกรณีที่ฐานข้อมูลมีการอัพเดทใน LINQ และ Entity Framework (Refresh Query in LINQ)

    จากปัญหาที่เคยเจอในกรณีที่ฐานข้อมูลมีการอัพเดทไปแล้ว พอ Select ข้อมูลออกมาข้อมูลไม่ refresh ในกรณีนี้จะยกตัวอย่างการใช้งานฟังก์ชัน reload ของ System.Data.Entity.Infrastructure

    public class DbEntityEntry where TEntity : class

    // Summary:
    // Reloads the entity from the database overwriting any property values with values
    // from the database. The entity will be in the Unchanged state after calling this
    // method.
    public void Reload();

    โดยการใช้งานนั้นจะยกตัวอย่างตามโค้ดด้านล่าง

    ProjectEntities pe = new ProjectEntities();

    var project = pe.PROJECT.Where(w => w.ID == projectID && w.YEAR == year).FirstOrDefault();

    if (project != null)

    {
    pe.Entry(project).Reload();

    }

    หวังว่าคงจะได้ช่วยโปรแกรมเมอร์ทีมีปัญหาเรื่องการ refresh ข้อมูลผ่าน LINQ และ Entity Framework

  • การสร้างเงื่อนไขแบบหลายตัวแปรในการค้นหาข้อมูลผ่าน LINQ (Multiple Search In LINQ)

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

    • ตัวแปร “ชื่อ/นามสกุล/เลขประจำตัวประชาชน”
      โดยใช้ control TextBox ที่ชื่อ ID=”txtSearch”
    • ตัวแปร “โครงการรับ” โดยใช้ control DropDownList ที่ชื่อ ID=”ddProject”
    • ตัวแปร “สถานะการตรวจเอกสาร”
      โดยใช้ control DropDownList ที่ชื่อ ID=”ddStatus”

    จากนั้นเราสร้าง Entity ยกตัวอย่างเป็น UploadEntities ซึ่งในที่นี้ สร้าง DbSet ที่เชื่อมต่อฝั่งฐานข้อมูลยกตัวอย่างเป็น V_REGISTRATION_UPLOAD ผ่าน Entity Framework 4.5 จากนั้นใช้ LINQ ในการเขียนเงื่อนไข ยกตัวอย่างตามโค้ดด้านล่าง

    project = ddProject.SelectedValue;

    status = ddStatus.SelectedValue;

    search = txtSearch.Text.Trim();

    outList = ue.V_REGISTRATION_UPLOAD

    .Where(w => (w.STUD_FNAME.Contains(search) || w.STUD_LNAME.Contains(search) || w.CITIZEN_ID.Contains(search)) || string.IsNullOrEmpty(search))

    .Where(x => x.PROJECT_ID == project || string.IsNullOrEmpty(project))

    .Where(y => y.APPROVED_STATUS == status || string.IsNullOrEmpty(status)) .ToList();

    gvUploadedList.DataSource = outList;

    gvUploadedList.DataBind();

    แสดงการใช้งานฟังก์ชัน Where ของ LINQ ในส่วนของ code behide
    ผลลัพธ์ที่ได้จากการเรียกใช้เงื่อนไขฟังก์ชัน Where ของ LINQ

    สรุปได้ว่าการนำฟังก์ชัน Where ของ LINQ มาใช้งานนั้น ทำให้โปรแกรมเมอร์สะดวกและลดการเขียนโค้ดให้ง่ายขึ้นจากเมื่อก่อนที่ต้องตรวจสอบเงื่อนไขเป็นแบบทีละเงื่อนไข

  • Remote Debugging ASP.NET application

    ในการพัฒนาเว็บแอพพลิเคชัน การที่จะ publish เว็บขึ้น server ผู้พัฒนาย่อมมีการทดสอบการทำงานของแต่ละฟังก์ชันแต่ละ process ผ่าน localhost ก่อนอยู่แล้ว ว่าสามารถทำงานได้ถูกต้อง ไม่พบ error หรือปัญหาใดๆ แต่ในบางครั้งพบว่า เมื่อ publish เว็บไปแล้ว กลับพบ error ในบางฟังก์ชัน ทั้งที่ฟังก์ชันนั้นผ่านการทดสอบบน localhost ว่าทำงานถูกต้องแล้ว ซึ่งอาจเกิดจาก Environment ในตอนที่เรา run ที่ localhost กับบน server ไม่เหมือนกัน หรือฐานข้อมูลที่ทดสอบกับฐานข้อมูลที่ใช้งานจริงมีข้อมูลที่ conflict กันอยู่ เป็นต้น ซึ่งบทความนี้ผู้เขียนจะมาแนะนำ Remote Debugging Tool สำหรับผู้พัฒนาเว็บด้วย ASP.NET

    Remote Debugging Tool

    คือ เครื่องมือที่ใช้ในการ remote debug สำหรับ ASP.NET แอพพลิเคชัน ซึ่งเป็นเครื่องมือขนาดเล็กที่มีชื่อว่า “Msvsmon.exe” ที่ให้เราสามารถ debug  code เพื่อหา error โดยการ remote จาก Visual Studio ไปยัง IIS server

     

    ติดตั้ง Remote Debugging Tool บน Windows Server

    download ตัวติดตั้ง Remote tools ตาม version ของ Visual Studio ที่เราใช้งาน เมื่อ download มาเรียบร้อยแล้วให้ทำการติดตั้ง โดยคลิกขวาที่ไฟล์ติดตั้ง เลือก Run as administrator จะปรากฎหน้าต่างดังรูป

     

    ให้เลือก Configure remote debugging จะเสร็จสิ้นขั้นตอนการติดตั้งและจะปรากฏหน้าต่าง Remote Debugger

    ซึ่งตอนนี้ Remote debugger ทำงานแล้ว โดยรอการเชื่อมต่อจากฝั่ง Visual Studio (ภายหลัง สามารถเรียกใช้งาน Remote Debugger ได้จาก Start menu)

    Attach Process จาก Visual Studio เครื่องพัฒนา

    1. เปิด Visual Studio ขึ้นมา และเลือก project ที่จะใช้งาน
    2. ที่แถบเมนูด้านบน เลือก Debug >> Attach to Process
    3. ปรากฎหน้าต่าง Attach to Process ดังรูป ในส่วนของ Qualifier ให้ระบุชื่อหรือ IP ของเครื่อง server ที่ web เราวางอยู่ และกดปุ่ม Refresh ด้านล่างเพื่อ connect ไปยังเครื่อง server ดังรูป

    4. เลือก process ที่ชื่อ w3wp.exe โดยสังเกตที่คอลัมน์ User Name เป็น path ของ site ที่เราต้องการทดสอบ หลังจากนั้นกดปุ่ม Attach ดังรูป

    5. เปิด browser และพิมพ์ url ไซต์ของเรา http://<remote computer name>

    6. เลือกมาร์กจุด breakpoint ที่จะ debug code ตามต้องการ เราก็จะสามารถ debug code โดยการ remote ไปยังเครื่อง server ที่เราได้ deploy เว็บของเราไปแล้วได้เหมือนการ debug บนเครื่อง localhost ที่เราพัฒนาค่ะ

     

    แหล่งข้อมูลอ้างอิงhttps://msdn.microsoft.com/en-us/library/mt621540.aspx

     

  • ทำอย่างไรให้เว็บไซต์ที่เราพัฒนาสามารถอัพโหลดไฟล์แบบคราวละหลายไฟล์ได้โดยไม่จำกัดจำนวน ด้วย ASP.NET(C#)

              ในการพัฒนาเว็บไซต์ บางครั้งอาจมีความจำเป็น หรือความต้องการจากผู้ใช้ที่ต้องการให้เว็บไซต์ดังกล่าวมีฟังก์ชั่นการทำงานในส่วนของการอัพโหลดไฟล์เพื่อแนบไฟล์เข้าไปในระบบและบันทึกลงฐานข้อมูล เพื่อให้สามารถเรียกดูข้อมูลการแนบไฟล์ดังกล่าวได้ในภายหลัง การอัพโหลดไฟล์จึงถือเป็นอีกหนึ่งฟังก์ชั่นการทำงานที่นักพัฒนาเว็บไซต์ควรทราบไว้ ซึ่งลักษณะการทำงานโดยทั่วไปส่วนใหญ่เราจะใช้ Control ที่มีเรียกว่า “FileUpload” แบบอัพโหลดครั้งละ 1 ไฟล์ และหากมีมากกว่านั้นก็จะมีการสร้างตัว FileUpload มาวางไว้ในหน้าจอเพิ่มตามจำนวนที่ต้องการแบบตายตัว เช่น หากในหน้าจอดังกล่าวต้องการสามารถให้ทำการอัพโหลดไฟล์ได้ไม่เกิน 5 ไฟล์ต่อการอัพโหลดแต่ละครั้งก็จะมีการลากคอนโทรล FileUpload มาวางไว้ในหน้าจอจำนวน 5 ตัว เพื่อให้สามารถรองรับความต้องการในการอัพโหลดไฟล์ของผู้ใช้ได้ แต่ผู้เขียนพบว่าการทำงานดังกล่าวอาจไม่รองรับความต้องการในการทำงานของผู้ใช้ที่จะเพิ่มไฟล์ได้ครั้งละหลายๆไฟล์โดยไม่จำกัดและผู้พัฒนาไม่จำเป็นต้องระบุหรือสร้างคอนโทรล FileUpload มาวางในหน้าจอในจำนวนที่ตายตัวโดยไม่จำเป็น โดยผู้ใช้สามารถคลิกปุ่มเพื่อเพิ่มจำนวนในการอัพโหลดไฟล์แต่ละครั้งได้เองเพื่อความยืดหยุ่นในการใช้งาน ในบทความนี้ ผู้เขียนจึงขอเสนอแนวทางในการพัฒนาเว็บไซต์โดยใช้ ASP.NET(C#) ในแบบที่มีการอัพโหลดไฟล์ได้คราวละหลายๆไฟล์ในแบบไม่ต้องจำกัดจำนวนคอนโทรล FileUpload ในหน้าจอโดยมีการระบุจำนวนไฟล์ที่สามารถอัพโหลดได้ในแต่ละครั้งโดยผู้พัฒนาแบบตายตัวอย่างเช่นที่กล่าวไว้ข้างต้น

              หลังจากที่ได้มีการศึกษาเพิ่มเติม ผู้เขียนพบว่าใน .NET Framework เวอร์ชั่น 4.5 นั้นจะมีการเพิ่ม Feature การทำงานในส่วนนี้ให้กับคอนโทรล FileUpload ไว้แล้วผ่าน Properties ที่เรียกว่า AllowMultiple ซึ่งจะทำให้สะดวกต่อการพัฒนาและสามารถลดปัญหาดังกล่าวข้างต้นได้ แต่สำหรับเวอร์ชั่นที่ต่ำกว่ายังคงต้องมีการปรับปรุงพัฒนาเพิ่มเติมเอง ในบทความนี้จึงขอยกตัวอย่างการพัฒนาทั้ง 2 แบบในเบื้องต้นโดยจะเน้นไปในแบบเวอร์ชั่นที่ต่ำกว่า 4.5 เพื่อให้ผู้อ่านได้นำไปเป็นแนวทางในการพัฒนาต่อไป ดังนี้
    1. การอัพโหลดไฟล์คราวละหลายๆไฟล์โดยใช้ .Net Framework เวอร์ชั่นที่ต่ำกว่า 4.5

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

    1) ออกแบบหน้าจอการทำงานในฝั่ง Client  ทำการออกแบบหน้าจอการทำงานไว้ในเบื้องต้น โดยมีการสร้างคอนโทรล FileUpload มาตั้งต้นไว้ 1 ตัว และมีปุ่มเพื่อให้ทำการเพิ่มคอนโทรล FileUpload ได้เองอัตโนมัติโดยตัวผู้ใช้เอง และปุ่มที่ใช้ในการอัพโหลดไฟล์จากคอนโทรล FileUpload ทั้งหมด ดังนี้

    <body>
    <form id="form1" runat="server">
    <div>
     <div id="fileUploadarea" class="Divborder">
      <div id='divfirstUpload'><br /><asp:FileUpload ID="fuMultiple" runat="server" CssClass="fileUpload" onchange="javascipt:FileValidate(this,5);" />&nbsp;  <input style="display:inline;" id="BtnRemove" type="button" value="Remove" onclick="DelFileUpload('divfirstUpload');" /> 
    </div> 
    </div>
    <br /> 
    <div>&nbsp; 
    <input style="display:inline; background-color: #A4EDFF; color: #333333; width: 150px; font-weight: bold;" id="btnAddMoreFiles" type="button" value="Add more files" onclick="AddMoreFilesWithMax(6);" />
    &nbsp; <asp:Button ID="BtnUpload" runat="server" onclick="BtnUpload_Click" Text="Upload" OnClientClick="return ValidateFileUpload();" BackColor="#3399FF" Font-Bold="True" ForeColor="White" Width="150px" /> 
    <br> 
    <asp:GridView ID="gvResult" runat="server" AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333" GridLines="None" Width="410px">
    <RowStyle BackColor="#EFF3FB" /> 
    <Columns> 
    <asp:BoundField DataField="Name" HeaderText="ชื่อไฟล์"> 
    <ItemStyle Width="200px" /> </asp:BoundField> 
    <asp:BoundField DataField="FileSize" HeaderText="ขนาดไฟล์(KB)"> 
    <ItemStyle HorizontalAlign="Right" Width="120px" /> 
    </asp:BoundField> 
    </Columns> 
    <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> 
    <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" /> <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> 
    <EditRowStyle BackColor="#2461BF" /> <AlternatingRowStyle BackColor="White" /> 
    </asp:GridView> 
    </div> 
    </div> 
    </form> 
    </body>

    2) ในส่วนของจาวาสคริปต์ที่ใช้เป็นตัวช่วยในการตรวจสอบ โดยจะแยกเป็นแต่ละ function ดังนี้

    2.1) ฟังก์ชั่นที่ใช้ในการเพิ่มไฟล์ได้ไม่จำกัด โดยใช้ชื่อว่า AddMoreFiles()

    <script language="javascript" type="text/javascript">
    
    function AddMoreFiles() {
    
      if (!document.getElementById && !document.createElement)
        return false;
    ////อ้างอิงถึง div ที่มีชื่อว่าfileUploadarea
      var fileUploadarea = document.getElementById("fileUploadarea"); 
      if (!fileUploadarea) return false; 
       var newLine = document.createElement("br"); fileUploadarea.appendChild(newLine); 
       var newFile = document.createElement("input"); 
       newFile.type = "file"; newFile.setAttribute("class", "fileUpload");
    
    /////กำหนดค่าเริ่มต้นให้กับตัวแปร AddMoreFiles.lastAssignedId 
    เพื่อใช้ในการกำหนด ID ให้กับคอนโทรลที่สร้างขึ้น และเป็นตัวนับ
      if (!AddMoreFiles.lastAssignedId) 
        AddMoreFiles.lastAssignedId = 1;
    
     //////กำหนดค่าแอททริบิวต์ต่างๆให้กับ FileUpload ที่ทำการสร้างใหม่ ในทีนี้คือ id และ name
        newFile.setAttribute("id", "FileUpload" + AddMoreFiles.lastAssignedId);  
        newFile.setAttribute("name", "FileUpload" + AddMoreFiles.lastAssignedId); 
    
     //////สร้าง div ขึ้นมาใหม่ และกำหนดค่าแอททริบิวต์ต่างๆให้กับ div ที่ทำการสร้างใหม่ 
    ในทีนี้คือ id และ เพิ่มคอนโทรลที่สร้าง tag ไว้ด้านบนเข้ามาไว้ใน div 
        var div = document.createElement("div"); 
        div.appendChild(newFile); 
        div.setAttribute("id", "div" + AddMoreFiles.lastAssignedId); 
    
    //////เพิ่ม div ที่สร้างใหม่ไปยัง div ที่มีชื่อว่า fileUploadarea และเพิ่มค่าของตัวแปร AddMoreFiles.lastAssignedId   
        fileUploadarea.appendChild(div); 
        AddMoreFiles.lastAssignedId++; 
    }

    2.2) ฟังก์ชั่นที่ใช้ในการเพิ่มไฟล์ได้โดยมีการระบุจำนวนสูงสุดที่ยอมให้อัพโหลดแต่ละครั้ง ในกรณีที่ต้องการระบุค่าจำนวนไฟล์ที่ต้องการให้ผู้ใช้สามารถอัพโหลดได้สูงสุดในแต่ละครั้ง สามารถปรับแก้เพิ่มเติมจากฟังก์ชั่นก่อนหน้านี้ โดยการส่งพารามิเตอร์จำนวนมากสุดที่ยอมให้สร้างตัวอัพโหลดไฟล์ได้ โดยใช้ชื่อฟังก์ชั่นว่า AddMoreFilesWithMax(x) ดังนี้

    function AddMoreFilesWithMax(x) 
    { //////กำหนดค่าให้กับ AddMoreFiles.lastAssignedId 
    เพื่อเป็นค่าตั้งต้นในการไปเซทค่า id และ name ของคอนโทรลที่สร้างใหม่ 
    โดยในที่นี้เริ่มต้นที่ 2 เนื่องจากเดิมมีคอนโทรลอัพโหลดไฟล์อยู่เดิมแล้ว 1 ตัว
      if (!AddMoreFiles.lastAssignedId)
          AddMoreFiles.lastAssignedId = 2;
    
       if (!document.getElementById && !document.createElement)
          return false; 
      var fileUploadarea = document.getElementById("fileUploadarea");
        if (!fileUploadarea) return false;
    
    /////เป็นการตรวจสอบเงื่อนไขว่ายังไม่เกินจำนวนมากสุดที่ระบุไว้ผ่านพารามิเตอร์ x
          if (AddMoreFiles.lastAssignedId <= x) 
          { 
          var newLine = document.createElement("br"); 
    
          //สร้างปุ่ม Remove เพื่อใช้ลบคอนโทรล FileUpload ที่สร้างขึ้นออกโดยผู้ใช้ 
          var newbtnDel = document.createElement("input"); 
          newbtnDel.setAttribute("id", "btnDelUpload" + AddMoreFiles.lastAssignedId);
          newbtnDel.setAttribute("name", "btnDelUpload" + AddMoreFiles.lastAssignedId); 
          newbtnDel.setAttribute("value", "Remove");   
          newbtnDel.type = "button"; 
    
         //////สร้างคอนโทรล FileUpload ขึ้นใหม่และกำหนดแอททริบิวต์ต่างๆให้
          var newFile = document.createElement("input"); 
          newFile.type = "file"; 
          newFile.setAttribute("class", "fileUpload");
          newFile.setAttribute("id", "FileUpload" + AddMoreFiles.lastAssignedId); 
          newFile.setAttribute("name", "FileUpload" + AddMoreFiles.lastAssignedId);
    
    //////เป็นส่วนของการเรียกฟังก์ชั่นในการตรวจสอบไฟล์ที่ทำการอัพโหลดว่าถูกต้องตรงตามเงื่อนไขหรือไม่
         เมื่อมีการเลือกไฟล์ใดๆจากผู้ใช้ ซึ่งจะพูดถึงในส่วนถัดไปในการสร้างฟังก์ชั่น FileValidate(ctrl,maxVal) 
          newFile.setAttribute('onchange', 'javascipt:FileValidate(this,5);');
    
    //////สร้าง div ขึ้นมาใหม่ และกำหนดค่าแอททริบิวต์ต่างๆให้กับ div ที่ทำการสร้างใหม่ 
    ในทีนี้คือ id และ เพิ่มคอนโทรลที่สร้าง tag ไว้ด้านบนเข้ามาไว้ใน div 
          var div = document.createElement("div");
          div.appendChild(newLine);
          div.appendChild(newFile);
    //////เพิ่มช่องว่างระหว่างคอนโทรลอัพโหลดไฟล์และปุ่ม Remove เพื่อความสวยงาม
          div.appendChild(document.createTextNode('\u00A0'));
          div.appendChild( newbtnDel);
          div.setAttribute("id", "div" + AddMoreFiles.lastAssignedId);
    
    //////เพิ่ม event การคลิกปุ่ม Remove เพื่อให้ไปเรียกใช้ฟังก์ชั่น DelFileUpload
    โดยส่งค่า id ของ div ที่ต้องการให้ลบออกซึ่งจะกล่าวถึงในส่วนถัดไปของการสร้างฟังก์ชั่น DelFileUpload(divid);
          newbtnDel.setAttribute('onclick', 'javascipt:DelFileUpload(\'' + "div" + AddMoreFiles.lastAssignedId + '\');');
    
          fileUploadarea.appendChild(div);
          AddMoreFiles.lastAssignedId++; } 
    
    ///////กรณีที่มีจำนวนตัวอัพโฟลดไฟล์ที่ถูกสร้างขึ้นเองเกินกว่าที่กำหนด(ค่าพารามิเตอร์ x) 
    จะแสดงข้อความให้ทราบ
     else { 
           alert("ขออภัย ท่านสามารถอัพโหลดไฟล์ได้สูงสุดครั้งละไม่เกิน " + x + " ไฟล์ต่อการอัพโหลดแต่ละครั้ง กรุณาลองใหม่อีกครั้ง"); 
          return false;
           } 
          }

    2.3) ฟังก์ชั่นที่ใช้ในการตรวจสอบขนาดของไฟล์ โดยมีการระบุขนาดสูงสุดที่ยอมให้อัพโหลดแต่ละครั้ง โดยใช้ชื่อฟังก์ชั่นว่า FileValidate(ctrlFile,MaxSize)

    ////เป็นการประกาศตัวแปรที่ใช้ในการตรวจสอบชนิดของไฟล์ที่ยอมให้อัพโหลดได้
    var _validFileExtensions = [".jpg", ".jpeg", ".bmp", ".gif", ".png", ".pdf"];
    function FileValidate(ctrlFile,MaxSize) 
    {//////พารามิเตอร์ที่รับเข้ามาคือตัวคอนโทรลของการอัพโหลดไฟล์ที่ต้องการตรวจสอบ(ctrlFile) 
    และขนาดสูงสุดของไฟล์ที่ยอมให้อัพโหลดในแต่ละคอนโทรล(MaxSize)
    
    ////ดึงค่าชื่อของไฟล์ที่อัพโหลดที่ต้องการตรวจสอบ
      var sFileName = ctrlFile.value;
    
    /////ตรวจสอบเมื่อพบชื่อไฟล์ที่ดึง
       if (sFileName.length > 0) 
       { 
         var blnValid = false; 
    //////////ตรวจสอบนามสกุลของไฟล์ที่ดึงมาจากชื่อไฟล์กับค่าของนามสกุลไฟล์ที่ยอมให้อัพโหลด
    จากตัวแปร _validFileExtensions
         for (var j = 0; j < _validFileExtensions.length; j++) 
          { var sCurExtension = _validFileExtensions[j]; 
           if (sFileName.substr(sFileName.length - sCurExtension.length, sCurExtension.length).toLowerCase() == sCurExtension.toLowerCase()) 
           { 
             blnValid = true; 
             break; 
            } 
          }   
    //////กรณีไฟล์ดังกล่าวมีนามสกุลที่แตกต่างจากที่กำหนดจะแสดงข้อความแจ้งเตือน 
    และล้างค่าของชื่อไฟล์ในคอนโทรลอัพโหลดไฟล์ตัวดังกล่าว
         if (!blnValid) 
         { alert("ไม่สามารถอัพโหลดไฟล์ดังกล่าวได้ เนื่องจากรองรับเฉพาะไฟล์ที่มีนามสกุลดังนี้เท่านั้น: " + _validFileExtensions.join(", ")); 
          ctrlFile.value = ""; 
          return false; 
         } 
         else 
         {
    /////หากไฟล์ที่อัพโหลดมีนามสกุลไฟล์ตามที่ระบุ จะทำการตรวจสอบขนาดของไฟล์ว่าไม่เกินจากขนาดสูงสุด
    ที่กำหนดหรือไม่ โดยมีการคำนวณหน่วยเป็น MB
           var fileSize = parseFloat(ctrlFile.files[0].size / 1048576).toFixed(2);
    
    /////หากขนาดของไฟล์เกินกว่าที่กำหนดจะแสดงข้อความแจ้งเตือน และล้างค่าไฟล์ที่ต้องการอัพโหลด
    ในคอนโทรลอัพโหลดไฟล์ดังกล่าว
           if (fileSize > MaxSize) 
            { alert(" ขออภัย ขนาดของไฟล์ที่ต้องการอัพโหลดมีขนาดใหญ่เกินกว่าทีกำหนด(" + MaxSize + " MB)"); 
              ctrlFile.value = ""; 
              return false;   
           } 
         }   
        }   
      return true; 
      }

    2.4) ฟังก์ชั่นที่ใช้ในการตรวจสอบว่ามีการเลือกไฟล์ที่ต้องการอัพโหลดแล้วหรือไม่ เมื่อมีการกดปุ่ม “Upload” โดยใช้ชื่อฟังก์ชั่นว่า ValidateFileUpload() 

    function ValidateFileUpload() { 
    //////เป็นการค้นหาคอนโทรลไฟล์อัพโหลดที่มีในหน้าจอโดยใช้ tag input 
    และตรวจสอบที่มี type เป็น "file" ใส่ไว้ในตัวแปร arrInputs
        var arrInputs = document.getElementsByTagName("input");
        var blnValid; 
        var oInput;
        for (var i = 0; i < arrInputs.length; i++)
         { oInput = arrInputs[i]; 
          if (oInput.type == "file") 
          { 
           var sFileName = oInput.value; 
    
     ///////หากพบตัวอัพโหลดไฟล์ตัวใดที่ยังไม่ได้ทำการเลือกไฟล์ไว้ จะทำการแสดงข้อความแจ้งเตือนให้ทราบ
           if (sFileName.length == 0) 
             { blnValid = false; 
              alert("ขออภัย ไม่สามารถอัพโหลดไฟล์ได้ เนื่องจากพบว่ามีบางไฟล์ไม่ได้ถูกเลือก กรุณาลองใหม่อีกครั้ง"); 
              return false;   
             } 
          }   
         }   
    return true; 
    }

    2.5) ฟังก์ชั่นที่ใช้ในการลบคอนโทรลที่ใช้ในการอัพโหลดไฟล์ โดยใช้ชื่อฟังก์ชั่นว่า DelFileUpload(dv) ซึ่งจะส่งพารามิเตอร์เป็น id ของ div ตัวที่ต้องการให้ลบ และถูกเรียกใช้เมื่อมีการกดปุ่ม “Remove”

    function DelFileUpload(dv) 
    { ////ค้นหา div ตาม id ที่ส่งเข้ามา
       var elem = document.getElementById(dv); 
      ///ทำการลบ div นั้นออกจากหน้าจอและลดค่าของ AddMoreFiles.lastAssignedId ลง 1
    เพื่อใช้ในการคำนวณค่าสูงสุดและกำหนด tag ของ id และ name ของคอนโทรลที่สร้างใหม่ต่อไป
       elem.parentNode.removeChild(elem); 
       AddMoreFiles.lastAssignedId = AddMoreFiles.lastAssignedId - 1; 
    }

    3) ในฝั่งเซฺิร์ฟเวอร์ (C#)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    /////เป็น namespace ที่ต้องอ้างอิงเพิ่มเพื่อใช้ในการจัดการข้อมูลกับ datatable
    using System.Data;
    /////เป็น namespace ที่ต้องอ้างอิงเพิ่มเพื่อใช้ในการจัดการเกี่ยวกับการอัพโหลดไฟล์
    using System.IO;
    public partial class MultipleUpload : System.Web.UI.Page
    {
    protected void BtnUpload_Click(object sender, EventArgs e)
    {
      try
      {
    
     //////สมมุติเพื่อนำไปประยุกต์ใช้กับการบันทึกลงฐานข้อมูลต่อไป 
    โดยสร้างโครงสร้าง datatable ที่ชื่อว่า dtFiles 
         DataTable dtFiles = new DataTable();
         dtFiles.Columns.AddRange(new DataColumn[3] { new DataColumn("Id", typeof(int)),
         new DataColumn("Name", typeof(string)),
         new DataColumn("FileSize",typeof(string)) });
    
    ////เป็นการดึงคอนโทรลในการอัพโหลดไฟล์ที่ถูกสร้างขึ้นและวนรอบในการบันทึกไฟล์ดังกล่าว
     HttpFileCollection hfc = Request.Files;
     for (int i = 0; i < hfc.Count; i++)
     {
        HttpPostedFile hpf = hfc[i];
       if (hpf.ContentLength > 0)
        {
    //////บันทึกไฟล์ตามที่ระบุไว้
        hpf.SaveAs(Server.MapPath("~/uploads/") + System.IO.Path.GetFileName(hpf.FileName));
    
    ////เพิ่มข้อมูลลงไปใน datatable ที่สร้างไว้ สำหรับการใช้งานจริงอาจเป็นการติดต่อเพื่อบันทึกลงฐานข้อมูล 
        dtFiles.Rows.Add(i,hpf.FileName, hpf.ContentLength / 1024);
         }
       }
     if (dtFiles.Rows.Count > 0) 
     {
    //////ดึงข้อมูลจาก datatable มาแสดงในกริดวิว
        gvResult.DataSource = dtFiles;
        gvResult.DataBind();
     
     }
       }
      }
      catch (Exception)
      {
       throw;
       }
      }
    }
    

    เพิ่มเติม :
    หากต้องการตกแต่งเพื่อความสวยงามสามารถใส่ StyleSheet เพิ่มเติมได้ ดังตัวอย่างต่อไปนี้

    <style type="text/css">
      .fileUpload
      {
       width:255px;
       font-size:11px;
       color:#000000;
       border:solid;
       border-width:1px;
       border-color:#7f9db9;
       height:17px;
      }
     .Divborder
      {
       border: 2px solid;
       border-radius: 5px;
       padding:10px;
       width:390px;
      }
    </style>
    

    ตัวอย่างผลลัพธ์ที่ได้

    1) แสดงหน้าจอ โดยแรกเริ่มมีอัพโหลดไฟล์ตั้งต้น 1 ตัว

    uploadsingle

    2) แสดงหน้าจอเมื่อมีการกดปุ่ม “Add more files” uploadmulti2

    3) แสดงผลลัพธ์หลังจากบันทึกข้อมูลเรียบร้อยแล้วmultipleResult2

    2. การอัพโหลดไฟล์คราวละหลายๆไฟล์โดยใช้ .Net Framework เวอร์ชั่น 4.5  ซึ่งในบทความนี้จะไม่ลงรายละเอียดมากนัก ผู้อ่านสามารถนำไปประยุกต์เพิ่มเติมได้ โดยจะอธิบายทีละขั้นตอนคร่าวๆ ดังนี้

    1) การออกแบบในหน้าจอฝั่ง Client

    <body>
    <form id="form1" runat="server">
    <div>
    <asp:FileUpload ID="file_upload" runat="server" AllowMultiple="true" />
    <asp:Button ID="btnFileUpload" runat="server" Text="Upload" OnClick="btnFileUpload_Click" />
    <asp:Label ID="lblUploadStatus" runat="server"></asp:Label><br />
     <asp:GridView ID="gvResult" runat="server" AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333" GridLines="None" Width="410px">
     <AlternatingRowStyle BackColor="White" />
     <Columns>
     <asp:BoundField DataField="Name" HeaderText="ชื่อไฟล์">
     <ItemStyle Width="300px" />
     </asp:BoundField>
     <asp:BoundField DataField="filesize" HeaderText="ขนาดไฟล์(KB)">
     <ItemStyle HorizontalAlign="Right" Width="110px" />
     </asp:BoundField>
     </Columns>
     <EditRowStyle BackColor="#2461BF" />
     <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
     <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
     <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
     <RowStyle BackColor="#EFF3FB" />
     <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
     <SortedAscendingCellStyle BackColor="#F5F7FB" />
     <SortedAscendingHeaderStyle BackColor="#6D95E1" />
     <SortedDescendingCellStyle BackColor="#E9EBEF" />
     <SortedDescendingHeaderStyle BackColor="#4870BE" />
     </asp:GridView>
    </div>
    </form>
    </body>

    2) การพัฒนาในฝั่งเซิร์ฟเวอร์(C#)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.IO;
    using System.Data;
    namespace WebAppTest
    {
     public partial class MultipleUpload : System.Web.UI.Page
     {
     protected void btnFileUpload_Click(object sender, EventArgs e)
     {
     try
     {
    ///////การตรวจสอบขนาดไฟล์และไฟล์ต้องเป็นชนิด image/jpeg เท่านั้น
     if (file_upload.HasFile && file_upload.PostedFiles.All(x => x.ContentType == "image/jpeg" && x.ContentLength < 102400))
     {
    ///////ประกาศตัวแปร และกำหนดโครงสร้างของ datatable
     int i = 1;
     DataTable dtFiles = new DataTable();
     dtFiles.Columns.AddRange(new DataColumn[3] { new DataColumn("Id", typeof(int)),
     new DataColumn("Name", typeof(string)),
     new DataColumn("FileSize",typeof(string)) });
     foreach (var file in file_upload.PostedFiles)
     {
    //////บันทึกไฟล์ที่เลือก
       file_upload.SaveAs(Server.MapPath("~/Fileupload/") + Path.GetFileName(file.FileName));
    ////วนบันทึกข้อมูลไฟล์ที่อัพโหลดลงใน datatable  
        dtFiles.Rows.Add(i, file.FileName, file.ContentLength / 1024);
     }
    
    ////แสดงข้อความเมื่อการอัพโหลดเสร็จสมบูรณ์
       lblUploadStatus.Text = "บันทึกสำเร็จ";
    
    ////แสดงค่าที่ได้ลงในกริดวิว
     if (dtFiles.Rows.Count > 0)
     {
     gvResult.DataSource = dtFiles;
     gvResult.DataBind();
    
     }
     }
     else
     {
    ////แสดงข้อความแจ้งเตือนกรณีเกิดปัญหาไฟล์มีขนาดใหญ๋เกินกว่าที่กำหนดและไม่ใช่ชนิด image/jpeg
       lblUploadStatus.Text = "กรุณาเลือกไฟล์ที่ต้องการอัพโหลดให้เหมาะสม";
     }
     }
     catch (Exception ex)
     {
    ////แสดงข้อความแจ้งเตือนกรณีเกิดปัญหาในการอัพโหลดไฟล์
       lblUploadStatus.Text = "เกิดข้อผิดพลาดในการอัพโหลดไฟล์ :" + ex.Message;
     }
     }
     }
    }

    ตัวอย่างผลลัพธ์ที่ได้

    1) ก่อนทำการอัพโหลดไฟล์

    multiple4_5_0

    2) หลังอัพโหลดไฟล์ทั้งหมดเรียบร้อยแล้ว

    multiple4_5

    หมายเหตุ : หากกำหนดค่าให้กับ Properties ที่ชื่อว่า AllowMultiple=”false ตอนเลือกไฟล์ที่จะอัพโหลดจะสามารถเลือกได้เพียงไฟล์เดียวเท่านั้น

              จากบทความและตัวอย่างข้างต้น จะเห็นว่า ลักษณะผลลัพธ์ที่ได้จะคล้ายคลึงกัน แต่จะแตกต่างกันในส่วนของความซับซ้อนในการตรวจสอบความถูกต้องของข้อมูลไฟล์แนบที่รับเข้ามา ซึ่งผู้พัฒนาเองสามารถเลือกใช้วิธีที่ตนถนัดและขึ้นกับเวอร์ชั่นของ .NET Framework ที่กำลังพัฒนา รวมถึงเงื่อนไขของการตรวจสอบให้ตรงตามความต้องการของผู้ใช้มากที่สุด โดยเนื้อหาในบทความนี้เป็นเพียงแนวทางหนึ่งให้กับท่านในเบื้องต้นเท่านั้น ท่านสามารถนำไปประยุกต์ต่อยอดเพิ่มเติมได้ และผู้เขียนหวังเป็นอย่างยิ่งว่าบทความนี้จะเป็นประโยชน์กับผู้พัฒนาที่กำลังค้นหาวิธีการอัพโหลดไฟล์คราวละหลายไฟล์นี้อยู่เช่นกัน หากมีเนื้อหาส่วนใดผิดพลาด ผู้เขียนขออภัยไว้ ณ ที่นี้ด้วย ขอบคุณค่ะ ^^
    แหล่งข้อมูลอ้างอิง :
    http://www.aspsnippets.com/Articles/Uploading-Multiple-Files-using-JavaScript-Dynamic-FileUpload-Controls-in-ASP.Net.aspx
    http://www.codeproject.com/Articles/667604/Upload-multiple-files-in-asp-net
    http://www.c-sharpcorner.com/UploadFile/99bb20/upload-multiple-files-using-fileupload-control-in-Asp-Net-4/

  • การ Encrypt/Decrypt ข้อมูลในไฟล์ Web.config

    การเข้ารหัส (Encrypt) ไฟล์ Web.config ถือเป็นวิธีการหนึ่งในการช่วยเพิ่มความปลอดภัยและช่วยป้องกันการถูกโจมตีจากผู้บุกรุกในการเชื่อมต่อกับฐานข้อมูล เนื่องจากไฟล์ web.config เป็นที่รวมการ config ค่าต่างๆ ของ web application ของเราไว้ เช่น ข้อมูลรหัสผ่านสำหรับการเชื่อมต่อฐานข้อมูล (ConnectionString), AppSetting, คีย์ API หรือข้อมูลสำคัญอื่นๆ ที่เกี่ยวกับการตั้งค่าต่างๆ โดยบทความนี้นำเสนอการเข้ารหัสและการถอดรหัส (Encrypt/Decrypt) ไฟล์ Web.config ด้วยคำสั่งผ่าน Command line โดยใช้ tools ที่มากับ .NET Framework ดังนั้นเครื่องคอมพิวเตอร์ของเราจะต้องมีการติดตั้ง .NET Framework ไว้อยู่ก่อนแล้ว สำหรับข้อมูลในไฟล์ Web.config จะถูกแบ่งออกเป็น section หลายๆ section ด้วยกัน โดยผู้เขียนจะขอยกตัวอย่างการ Encrypt/Decrypt ข้อมูลในส่วนของ section<connectionString>ดังนี้

     

    ก่อนทำการ Encrypt Web.config

    ที่ section <connectionStrings> ซึ่งเก็บข้อมูล config ค่าต่างๆ ของ Database sever ไว้ เช่น user/password เป็นต้น

    <configuration>
    <connectionStrings>
    <add name="SqlServices" connectionString="Data Source=localhost;PASSWORD=1234;Integrated Security=SSPI;Initial Catalog=Northwind;" />
    </connectionStrings>
    </configuration>

     

    Encrypt Web.config

    1.เปิด Command Prompt ขึ้นมา (อย่าลืมเปิดแบบ Run as administrator ด้วยนะคะ)

    2.พิมพ์คำสั่ง ดังนี้(Version ของ.NET Framework ขึ้นอยู่กับที่ลงไว้ที่เครื่อง) :

    cd C:\Windows\Microsoft.NET\Framework\v4.0.30319

    step1

    3.เข้าไปที่ directory path ที่เก็บไฟล์ web.config แล้วพิมพ์คำสั่ง ดังนี้

    aspnet_regiis.exe -pef connectionStrings  C:\inetpub\wwwroot

    step2

    หมายเหตุ: “connectionString” เป็น case sensitive ตัวพิมพ์เล็ก พิมพ์ใหญ่ ต้องพิมพ์ให้ถูกต้อง

     

    หลังทำการ Encrypt Web.config

    หลังจากทำการ Encrypt แล้วจะเห็นว่า ที่ section connectionString จะเป็นรหัสที่ไม่สามารถอ่านเข้าใจได้

    <connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
       <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
         xmlns="http://www.w3.org/2001/04/xmlenc#">
         <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
           <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
             <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
             <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
               <KeyName>Rsa Key</KeyName>
             </KeyInfo>
             <CipherData>
               <CipherValue>SryXAF+wpnIpZ8P3HMP8ffMDBorz9j08/oX2vXDA+9LkMHY1i50qeCqYmOYnQXK4C6iNhyIZx9R+AcE7yY7AQeHzzPhZ/bZ04NPuOpd7wD+NL82CWeec/fToTIBbHvE6zNBgenUSE+8zTv9II357tsqpjH1xaII+zmZbgo5+fnhjAD8nVffqd+NQ0x+IXDwyBraeT50TlEXx4lJlAph7jqdglg1Xf/yjSTfwrOB2NcVIHVaVWN3CrelWgQKASftGXdDVingbRn2RXphyooTuVsZgJdzbFMpd7H6fJHggORSPwOud1ZU5vE4aNAMHDa4fb6FOA3I8R0urWD4sT34YRg==</CipherValue>
             </CipherData>
           </EncryptedKey>
         </KeyInfo>
         <CipherData>
           <CipherValue>iKttnP9CEMRfq+mhcHqN8f5GiwsySBLw0CWeiAxSQVIfoEQMNutubrRFruoNIb+m0XJnPL5FIypqqQ72dqZ4DSeQdUJwAVyO4HSlM5F3b+Jnogz9aMAuwdoww5QKdI94yH9fx8RwxhAzq1a9eApjLglAwTmKY84Wg/Lqpcn9wfvKyIZJyQaJCaLCEteGhB9bopjT9z+nmMZVQ2LrDEtKMEe1oltPoR8EvTel2/2jJ/gwvJK+vTw9sL2GMPJIoA/NZQWjR2CWaCGlEFIjmgqT8BjTwiSS6hEp90CEQVN14U9A71vXquH73X6ZAyIFScf2XJS1Y+iCaEFo6r8qIIiGdrAGUTgfa8r9EwHzb5emCzlQzEDWgzno5IUDxuMBNzJinFudgwPFkA/xhdAHcHnB+quFOGGKVZJw72o5Ix7IeWI1Frs2n8+JZMrpPNW5r4SHHLrMD/iPR5FSfDz/sQqR5f+3/N3i3aq0LX8NfthfYtS9N4oCdPBPbIReP1w05we1Q+PJMO4KVG4w3x8k9O7aT0zoi+5CuPgKR30IgbE0sL8fOeki5OKYfsscNMzV/6T23axh9Ky+axI9fAMarjjL5aeYfa8n6jeevpA4f2SmlkbvW6P72292Ihk3bD1HMghn7ibjZ2q6hLwTb9QWyQvSDjHPsqeoiuKBTNJZgsCqBA4QK8j6/9exufTsOe/SgTclHPfiXpSI7CgbaGd6JQ2P5QSDVwQNQPuaf4qFKsZdPWfdEGcCgxLVZtcU0Cd12AnpaWIpiVUjCz6pWl4YygEXsvntLQfLSQ7XX2Q8lA1DjqBOcDQXY9mMo6PvZi3IsqcdF/DEn931nwBPO09T2AqWJWuAaK/Vh4+olkVYFWuj/Eerp2UkG2ItJduUaRNWzXV9s1hQ/q/36S0RTxN0DgXPII6CowQWIV2d5ZYwSUKVgsiDM43GBPF4SFFJOUec37yzYv6XT3/BmDmNpq32a+VAUB/OP23k4mnTvm/Nay1Iy6E8sUOYSpCY1up6XAcFL1XsacpNKLpMTmH6LsROX9BhmdTNaWgCQaDNVNeAISoJ8HZ5lw/EX1f6Rtz4uWyvBfSOPPAWPkNQawexjVKl0FtR6fRSnAGMkgflURH5QNX5xm+y0sfZsNz9sAFZofoJLQ3rdV7ToFJE+JlEvPKRHFDVbxDCURQ5CynXFnqlLj+2LhBbyX0n6oHQwMgTTTIf/+PNcjOx3zBn6/V3T0PdD6fnjVtTDnbJzN7ct7SOuifW0OfCfyeKSs0IOzPm8BucZ4CODaTwjY1bz2kgGRTjuenCp1N1GIRhMJBIBJhWOs3nee6Y9DgtlpWc3SZRZeYkmOffT5xNwQeTLrIXvTZCByHZcN3+g8OG0HqlodUCbPgf8jK45dMeT8piyFwZem8Dotjz7mXOaJxF7C66SQKvFVfuJjXDmTbmdzqwt33z+w39TV8ueXGyB/5S1kpV/ul+c7LwTwked7bMJtFJpVwdFKFiUxMebDuu3vINlVSZJfo43SSmTD3rM2UXV8ol65KzlmacBhgCwXtLFg7/8uGQrrUVq9gqHsoPyEgt</CipherValue>
         </CipherData>
       </EncryptedData>
     </connectionStrings>
    

     

    Decrypt Web.config

    การถอดรหัส (Decrypt) ไฟล์ Web.config สามารถใช้คำสั่งเช่นเดียวกันกับการ Encrypt แต่เปลี่ยนจาก -pef เป็น -pdf ดังคำสั่งต่อไปนี้ :

    aspnet_regiis.exe -pdf connectionStrings  C:\inetpub\wwwroot

    หมายเหตุ: “connectionString” เป็น case sensitive ตัวพิมพ์เล็ก พิมพ์ใหญ่ ต้องพิมพ์ให้ถูกต้อง

    การ Decrypt จะทำได้เฉพาะที่เครื่องที่ทำการ encrypt เท่านั้น จะไม่สามารถ decrypt ไฟล์ web.config นี้จากเครื่องอื่นได้ หากเรานำไฟล์ไป Decrypt จากเครื่องอื่นก็จะพบ message Failed ดังรูป

    step3

    โดยการ Encrypt/Decrypt ด้วยการใช้คำสั่งผ่าน command line สามารถทำได้กับทุก section (ไม่เฉพาะ connectionString)ในไฟล์ Web.confog เพียงแต่เปลี่ยนคำสั่งในส่วนของ section ตามที่ต้องการ

     

    แหล่งข้อมูลอ้างอิง : https://msdn.microsoft.com/en-us/library/ff647398.aspx

  • ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C# (ภาคต่อ)

                 จากบทความที่แล้ว ผู้เขียนได้เขียนไว้เกี่ยวกับเรื่องวิธีการกำหนดจุดพิกัดบนแผนที่กันไปบ้างแล้ว ในหัวข้อ “ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C#” สำหรับในบทความนี้ผู้เขียนจึงขอพูดถึงในส่วนของการดึงค่าละติจูด ลองจิจูดของสถานที่ ซึ่งนับว่าเป็นส่วนประกอบสำคัญในการแสดงผลพิกัดบนแผนที่ ซึ่งเดิมทีแล้วนั้น ผู้ใช้อาจต้องค้นหาข้อมูลพิกัดดังกล่าวจาก Google map เองและนำพิกัดดังกล่าวมากรอกลงฐานข้อมูลหรือมาระบุเพื่อการแสดงพิกัดนั้นๆในการเขียนโปรแกรม คงเป็นการดี หากการแสดงผลพิกัดจากฐานข้อมูลนั้น จะมีตัวช่วยอำนวยความสะดวกให้กับผู้ใช้ในการดึงค่าละติจูด และลองจิจูดโดยการกรอกข้อมูลชื่อสถานที่ลงไปเพื่อใช้ในการค้นหา ซึ่งน่าจะเป็นประโยชน์และทำให้ผู้ใช้สามารถใช้งานได้ง่ายขึ้นในการนำพิกัดเหล่านั้นไประบุบนแผนที่นั่นเอง

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

    • การเรียกใช้เซอร์วิสของ Google Geocoding API  โดยการส่งพารามิเตอร์เป็นที่อยู่ของสถานที่ดังกล่าว

    ฝั่ง C#

        private void getLatAndLong()
        {
            try
            {
             ////เป็นการกำหนด url ที่จะใช้ในการเรียกเซอร์วิสของ Google Geocoding API 
    โดยมีการส่งค่าพารามิเตอร์เป็นข้อมูลที่อยู่
                string url = "http://maps.google.com/maps/api/geocode/xml?address=" + txtLocation.Text + "&sensor=false";
                WebRequest request = WebRequest.Create(url);
                using (WebResponse response = (HttpWebResponse)request.GetResponse())
                {
            ////ผลลัพธ์จะอยู่ในรูปแบบของ XML หรือ JSON และจะถูกอ่านให้อยู่ในรูปแบบ Dataset 
    โดยใช้ StreamReader
                     using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                    {
                        DataSet dsResult = new DataSet();
                        dsResult.ReadXml(reader);
           ////จัดทำโครงสร้างตาราง(datatable) ที่จะใช้ในการแสดงผลใน Gridview
                        DataTable dtCoordinates = new DataTable();
                        dtCoordinates.Columns.AddRange(new DataColumn[4] { new DataColumn("Id", typeof(int)),
                            new DataColumn("Address", typeof(string)),
                            new DataColumn("Latitude",typeof(string)),
                            new DataColumn("Longitude",typeof(string)) });
    
           ////ดึงค่าผลลัพธ์จากตารางต่างๆ เพื่อนำค่าที่จำเป็นมาแสดงผลตามต้องการ
                        foreach (DataRow row in dsResult.Tables["result"].Rows)
                        {
                            string geometry_id = dsResult.Tables["geometry"].Select("result_id = " + row["result_id"].ToString())[0]["geometry_id"].ToString();
                            DataRow location = dsResult.Tables["location"].Select("geometry_id = " + geometry_id)[0];
                             dtCoordinates.Rows.Add(row["result_id"],  row["formatted_address"], location["lat"], location["lng"]);
                        }
           ////แสดงผลข้อมูลของค่าที่ดึงมาได้ใน Gridview (ถ้ามี)
                        if (dtCoordinates.Rows.Count > 0)
                        {
                            gvLatLong.DataSource = dtCoordinates;
                            gvLatLong.DataBind();
                        }
                        else
                        {
                            gvLatLong.DataSource = null;
                            gvLatLong.DataBind();
                            ScriptManager.RegisterStartupScript(Page, Page.GetType(), "alert", "alert('Can not find Latitude and Longitude!'); ", true);
    
                        }
                    }
                }
            }
            catch (Exception ex) {
                ScriptManager.RegisterStartupScript(Page, Page.GetType(), "alert", "alert('Can not find Latitude and Longitude!'); ", true);
                gvLatLong.DataSource = null;
                gvLatLong.DataBind();
            }
        
        }
     protected void btnSearch_Click(object sender, EventArgs e)
     {
        getLatAndLong();
     }
    

                 จากโค้ดข้างต้น เป็นการค้นหาละติจูด-ลองจิจูดจากที่อยู่ของสถานที่นั้นๆ โดยใช้ Google Maps Geocoding API ซึ่งเซอร์วิสของ Google Geocoding API มีการส่งค่าโดยใช้ WebRequest โดยจะรับค่าที่อยู่ของสถานที่เป็นพารามิเตอร์และส่งกลับเป็นพิกัดและข้อมูลอื่นๆมาในรูปแบบของ XML หรือ JSON นั่นเอง และค่าที่ส่งกลับมาในรูปแบบ XML นั้นจะถูกอ่านให้อยู่ในรูปแบบ Dataset โดยใช้ StreamReader ซึ่งค่าที่ส่งกลับมานั้นจะมีด้วยกันหลายตาราง และมีการดึงค่าข้อมูลที่เกี่ยวข้องที่จะนำมาใช้เพิ่มลงในตารางที่ถูกสร้างขึ้นใหม่ และนำไปแสดงในกริดวิวนั่นเอง

    ฝั่ง .aspx (Client side)

    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
     <title>GetLatitude-Longitude</title>
    <!-- การอ้างอิงเพื่อให้สามารถเรียกใช้งาน Autocomplete ได้-->
     <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
    </head>
    <body>
     <form id="form1" runat="server">
     <div>
     <asp:TextBox ID="txtLocation" runat="server" Width="450px"></asp:TextBox><asp:Button ID="btnSearch" runat="server" Text="Search" OnClick="btnSearch_Click" />
     </div>
     <br />
     <div>
     <asp:GridView ID="gvLatLong" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None">
     <AlternatingRowStyle BackColor="White" />
     <EditRowStyle BackColor="#2461BF" />
     <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
     <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
     <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
     <RowStyle BackColor="#EFF3FB" />
     <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
     <SortedAscendingCellStyle BackColor="#F5F7FB" />
     <SortedAscendingHeaderStyle BackColor="#6D95E1" />
     <SortedDescendingCellStyle BackColor="#E9EBEF" />
     <SortedDescendingHeaderStyle BackColor="#4870BE" />
     </asp:GridView>
     </div>
     </form>
     <script type="text/javascript">
     google.maps.event.addDomListener(window, 'load', function () {
    ////การกำหนดคอนโทรล textbox ที่จะเพิ่มความสามารถ place auto-complete 
    เพื่อให้สามารถค้นหาสถานที่ตั้งได้ง่ายขึ้นเมื่อผู้ใช้พิมพ์ข้อมูลชื่อสถานที่ในกล่อง textbox
     var txtplaces = document.getElementById('<%= txtLocation.ClientID %>');
     var places = new google.maps.places.Autocomplete(document.getElementById('<%= txtLocation.ClientID %>'));
     });
     </script>
    </body>
    </html>
    

                 จากโค้ดข้างต้น นอกจากจะเป็นการเรียกใช้เซอร์วิสโดยการคลิกปุ่ม “btnSearch” และรับค่าข้อมูลที่อยู่จากกล่อง textbox แล้ว ยังมีการเพิ่มส่วนของการเพิ่มความสามารถในการค้นหาข้อมูล “ที่อยู่” โดยการกรอกข้อมูล “ชื่อสถานที่” โดยใช้ feature ที่มีของ Google Places API (หรืออาจเรียกว่า place Autocomplete ) เนื่องจากหากมีการเรียกใช้เซอร์วิสดังกล่าวโดยตรง ผู้ใช้จำเป็นที่จะต้องกรอกข้อมูล “ที่อยู่” เพื่อเป็นพารามิเตอร์ให้กับเซอร์วิสที่เรียกใช้ให้ส่งค่าผลลัพธ์กลับมา ซึ่งความน่าจะเป็นที่ผู้ใช้จะสามารถทราบข้อมูลชื่อสถานที่นั้นย่อมเป็นไปได้มากกว่าการทราบข้อมูลที่อยู่ของสถานที่นั้นๆ ผู้เขียนจึงนำมาประยุกต์ใช้งานเข้าด้วยกันกับการเรียกเซอร์วิสที่กล่าวไว้แล้วก่อนหน้านั่นเอง โดยจะแนะนำวิธีการใช้งานเพิ่มเติมในหัวข้อต่อจากนี้

     เพิ่มเติม :
      Place Autocomplete: เป็นfeature ที่มีของ Google Places API เปรียบเสมือนตัวช่วยคำค้นเมื่อมีการป้อนข้อมูลชื่อสถานที่ลงไป โดย feature ดังกล่าวจะแสดงข้อมูลสถานที่ที่ใกล้เคียงกับคำค้นขึ้นมาให้กับผู้ใช้ได้เลือกนั่นเอง

    โดยมีวิธีการเรียกใช้ place autocomplete  ดังนี้
    1. อ้างอิงการเพื่อเรียกใช้งาน Google Places API

    <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>

    2. กำหนดคอนโทรล textbox ที่ต้องการเพิ่มความสามารถ place autocomplete

     var input = document.getElementById('<%= txtSearch.ClientID %>');
     var div = document.getElementById('result');
     var autocomplete = new google.maps.places.Autocomplete(input);

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

    place_service1

    ผลลัพธ์

    place_service2

    เพิ่มเติม : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Net
                –  System.Data
                –  System.Text
    • แบบใช้ place Autocomplete ซึ่งเป็น feature ของ Google Places API ที่จะช่วยในการค้นหาที่อยู่จากชื่อสถานที่ได้และประยุกต์เพิ่มเติมเพื่อดึงค่ามาแสดงเมื่อมีการเลือกรายการสถานที่นั้นๆ
    <!DOCTYPE html>
    <html>
     <head>
    <!-- การอ้างอิงเพื่อให้สามารถเรียกใช้งาน Autocomplete ได้-->
     <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
     <title>Place Autocomplete</title>
     <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
     <meta charset="utf-8">
     </head>
     <body>
     <form runat="server">
     <asp:TextBox ID="txtSearch" runat="server" placeholder="Enter a location" Width="350px"></asp:TextBox> 
     <div id="result"></div>
     
     <script>
     function initialize() {
    ////การกำหนดคอนโทรล textbox ที่จะเพิ่มความสามารถ place auto-complete 
    เพื่อให้สามารถค้นหาสถานที่ตั้งได้ง่ายขึ้นเมื่อผู้ใช้พิมพ์ข้อมูลชื่อสถานที่ในกล่อง textbox
     var input = document.getElementById('<%= txtSearch.ClientID %>');
     var div = document.getElementById('result');
     var autocomplete = new google.maps.places.Autocomplete(input);
     google.maps.event.addListener(autocomplete, 'place_changed', function () {
    
    ////ดึงค่าที่ได้เมื่อมีการเลือกรายการจากสถานที่ที่อยู่ที่ขึ้นมา และนำไปใช้ในการแสดงผล
     var place = autocomplete.getPlace();
     if (!place.geometry) {
     return;
     }
     else {
    ////นำค่าที่ดึงได้มาแสดงผลในพื้นที่ที่ต้องการ (result)
     div.innerHTML = '<br><div><strong>' + place.name + '</strong><br>' +
     '<strong>Address :</strong> ' + place.formatted_address + '<br><strong>Latitude :</strong> ' + place.geometry.location.lat() + ' <strong>Longitude:</strong>' + place.geometry.location.lng() + '</div>';
     }
     });
     }
     ////สั่งรันฟังก์ชั่น initialize() ตอนมีการโหลดหน้าจอ
     google.maps.event.addDomListener(window, 'load', initialize); 
     </script>
     </form>
     </body>
    </html>
    

                 จากโค้ดข้างต้น เป็นการดึงค่าละติจูดและลองจิจูดโดยใช้ feature ที่มีของ Google Places API (place Autocomplete) เพื่ออำนวยความสะดวกให้กับผู้ใช้ในการพิมพ์ชื่อสถานที่ลงใน textbox โดย feature นี้จะมีความสามารถในการดึงข้อมูลสถานที่และชื่อที่ใกล้เคียงขึ้นมาแสดงให้ผู้ใช้เลือก(ตามที่ได้กล่าวมาแล้วข้างต้น) และได้มีการสร้าง event เพิ่มเติมเมื่อผู้ใช้เลือกรายการใดขึ้นมา ก็จะมีการเรียกใช้เมธอด getPlace() เพื่อดึงค่าข้อมูลต่างๆมาใช้งานต่อไปรวมถึงค่าละติจูดและลองจิจูดที่เราต้องการใช้ในการกำหนดพิกัดด้วย

    ตัวอย่างหน้าจอการค้นหาข้อมูลที่อยู่ เมื่อมีการพิมพ์คำค้น

    place_auto1

    ผลลัพธ์ที่ได้จากการค้นหา

    place_auto2

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

    แหล่งข้อมูลอ้างอิง :
    http://www.aspsnippets.com/Articles/Find-Co-ordinates-Latitude-and-Longitude-of-an-Address-Location-using-Google-Geocoding-API-in-ASPNet-using-C-and-VBNet.aspx
    https://developers.google.com/places/javascript/

  • Export ข้อมูลไฟล์ Excel ในแบบ Single และ Multiple sheet ด้วย ASP.NET(C#)

                ความเดิมตอนที่แล้ว ผู้เขียนได้พูดถึงความสำคัญของข้อมูล และวิธีการ Import ข้อมูลจากไฟล์ Excel กันไปพอสมควร สำหรับในบทความนี้ ผู้เขียนจะขอพูดถึงวิธีการส่งออกข้อมูล หรือที่เรามักเรียกกันติดปากว่า “Export ข้อมูล” กันบ้าง เพื่อให้ผู้พัฒนาที่มีความสนใจสามารถพัฒนาโปรแกรมได้ครบวงจรทั้งแบบนำเข้าและส่งออกข้อมูล โดยผู้เขียนจะไม่ขอพูดถึงในรายละเอียดที่ได้กล่าวไว้แล้วก่อนหน้า ผู้อ่านสามารถอ่านรายละเอียดเพิ่มเติมได้ในบทความ “เรียนรู้วิธีการ Import ข้อมูลในรูปแบบไฟล์ Excel ด้วย ASP.NET (C#)” สำหรับในบทความนี้ผู้เขียนจะเน้นในส่วนของการ Export ข้อมูลเท่านั้น ซึ่งจะมีการอธิบายใน 2 ลักษณะเช่นกัน คือ แบบ Single sheet และแบบ Multiple sheet เพื่อให้เห็นเป็นแนวทางและสามารถนำไปต่อยอดการพัฒนาเพิ่มเติมสำหรับแต่ละท่านได้

    กรณีส่งออกข้อมูล(Export) ซึ่งในบทความนี้จะแบ่งเป็น 2 ลักษณะ ดังนี้

    • การส่งออกข้อมูลแบบ Single-sheet ซึ่งมีขั้นตอนการทำงานไม่ซับซ้อนมากนัก ดังนี้
    protected void btnExport_Click(object sender, EventArgs e)
    
    {
    //// ตารางสมมติ สร้างขึ้นเพื่อให้ผู้พัฒนาเห็นภาพ หากเป็นกรณีใช้งานจริงจะเป็นข้อมูลที่ดึงจากฐานข้อมูลเพื่อ Export ในรูปแบบไฟล์ Excel
    DataTable table = new DataTable();
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("Latitude", typeof(decimal));
    table.Columns.Add("Longitude", typeof(decimal));
    table.Columns.Add("Description", typeof(string));
    table.Rows.Add("University1", 7.006923, 100.500238, "Desc1");
    table.Rows.Add("University2", 7.172661, 100.613726, "Desc2");
    
    
    
    StringBuilder sb = new StringBuilder();
    if (table.Rows.Count > 0)
    {
    string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), DateTime.Now.ToString("ddMMyyyyhhmmss") + ".xls");
    
    //// ลักษณะการตั้งค่าเพื่อเชื่อมต่อด้วย OleDb ซึ่งในกรณีนี้ไฟล์ Excel จะต้องมีนามสกุลเป็น .xls แต่หากเป็นนามสกุลแบบ .xlsx ต้องเปลี่ยนการกำหนดค่าให้เป็น
    conString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + 
    fileName + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';";  แทน
    
    string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileName + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";
    
    
     using (OleDbConnection con = new OleDbConnection(conString))
    {
    ////เขียนคำสั่งในการสร้างตาราง ซึ่งในที่นี้คือ WorkSheet ที่ต้องการ พร้อมทั้งกำหนดชื่อและชนิดของข้อมูลในแต่ละคอลัมน์
    string strCreateTab = "Create table University (" +
    " [Name] varchar(50), " +
    " [Latitude] double, " +
    " [Longitude] double, " +
    " [Description] varchar(200)) ";
    
    
    
    if (con.State == ConnectionState.Closed)
    {
    con.Open();
    }
    ////รันคำสั่งที่เขียนในการสร้างตาราง
    OleDbCommand cmd = new OleDbCommand(strCreateTab, con);
    cmd.ExecuteNonQuery();
    
    ////เขียนคำสั่งในการเพิ่มข้อมูล(insert) ข้อมูลในแต่ละฟิลด์ รวมทั้งประกาศพารามิเตอร์ที่ใช้ในการรับค่าข้อมูลที่อ่านได้
    string strInsert = "Insert into University([Name],[Latitude]," +
    " [Longitude], [Description]" +
    ") values(?,?,?,?)";
    
    OleDbCommand cmdIns = new OleDbCommand(strInsert, con);
    cmdIns.Parameters.Add("?", OleDbType.VarChar, 50);
    cmdIns.Parameters.Add("?", OleDbType.Double);
    cmdIns.Parameters.Add("?", OleDbType.Double);
    cmdIns.Parameters.Add("?", OleDbType.VarChar, 200);
    
    ////วนค่าที่ได้จากฐานข้อมูลและกำหนดค่าให้กับพารามิเตอร์และรันคำสั่งในการเพิ่มข้อมูลทีละรายการ
    foreach (DataRow  i in table.Rows)
    {
    cmdIns.Parameters[0].Value = i["Name"];
    cmdIns.Parameters[1].Value = i["Latitude"];
    cmdIns.Parameters[2].Value = i["Longitude"];
    cmdIns.Parameters[3].Value = i["Description"];
    cmdIns.ExecuteNonQuery();
    
    }
    }
    
    ////สร้างไฟล์ที่ต้องการดาวน์โหลดจากการอ่านที่ได้ทั้งหมด เพื่อบันทึกเป็นไฟล์ Excel ที่มีชื่อว่า University.xls
    
    byte[] content = File.ReadAllBytes(fileName);
    HttpContext context = HttpContext.Current;
    context.Response.BinaryWrite(content);
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    context.Response.AppendHeader("Content-Disposition", "attachment; filename=University.xls");
    Context.Response.End();
    }
    }
    • การส่งออกข้อมูลแบบ Multiple sheet ใช้ในกรณีที่มีข้อมูลที่ Export จำนวนมากและต้องการแบ่งข้อมูลที่มีอยู่ออกเป็น WorkSheet ย่อย ซึ่งหลักการทำงานจะคล้ายกับการส่งออกข้อมูลแบบ Single-sheet แต่จะซับซ้อนกว่าในส่วนของการวนค่าจากในฐานข้อมูลมาใส่ใน  WorkSheet โดยมีการกำหนดจำนวนแถวที่จะให้บันทึกในแต่ละ WorkSheet หากเกินค่าที่กำหนดจะสร้าง WorkSheet ใหม่และบันทึกข้อมูลลงไปใน WorkSheet นั้น
    static StringBuilder sqlInsert = new StringBuilder();
    static StringBuilder strScript = new StringBuilder();
    
    public static void ExportToMultipleXLSheets( System.String OutputFileName)
    {
    string connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
    mOutputFileName + ";Extended Properties='Excel 8.0'";
    OleDbConnection xlConn = new OleDbConnection(connstr);
    
    try
    {
    xlConn.Open();
    ////ตารางสมมติเช่นเดียวกับที่ได้กล่าวไว้ในแบบการส่งออกข้อมูลแบบ Single sheet
    DataTable table = new DataTable();
    table.Columns.Add("EmployeeID", typeof(string));
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("LastName", typeof(string));
    table.Columns.Add("Position", typeof(string));
    
    table.Rows.Add("0001", "Jack", "Rayman", "Programmer");
    table.Rows.Add("0002", "John", "Wicky", "Programmer");
    table.Rows.Add("0003", "Jenifer", "Wincy", "Programmer");
    table.Rows.Add("0004", "Kate", "Wrapper", "Manager");
    table.Rows.Add("0005", "Bella", "Cole", "Programmer");
    table.Rows.Add("0006", "Sandy", "Stick", "Programmer");
    table.Rows.Add("0007", "Tom", "Runner", "Programmer");
    
    ////เป็นการเรียกใช้เมธอดที่ใช้ในการเตรียมคำสั่งที่จะใช้วนสร้างตาราง/Worksheet
    PrepareScript(table);
    
    ////เป็นเมธอดที่ใช้ในการเริ่มต้นทำงานคำสั่งในการ Export โดยจะมีการคำนวณจำนวนแถวของข้อมูลว่าเกินที่ระบุไว้หรือไม่ หากเกินกำหนดจะสร้าง WorkSheet ใหม่นั่นเอง
    StartExport(table, xlConn);
    
    if (xlConn != null)
    {
    if (xlConn.State == ConnectionState.Open) xlConn.Close();
    xlConn.Dispose();
    }
    
    ////อ่านค่าและ Export ไปยังไฟล์ที่มีชื่อว่า ExportMultiSheetData.xls
    byte[] content = File.ReadAllBytes(mOutputFileName);
    HttpContext context = HttpContext.Current;
    context.Response.BinaryWrite(content);
    context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    context.Response.AppendHeader("Content-Disposition", "attachment; filename=ExportMultiSheetData.xls"  );
    context.Response.End();
    
    }
    catch (Exception exp)
    {
    throw new Exception("ImportToMultipleXLSheets", exp.InnerException);
    }
    finally
    {
    if (xlConn != null)
    {
    if (xlConn.State == ConnectionState.Open) xlConn.Close();
    xlConn.Dispose();
    }
    }
    }
    o เมธอดในการเตรียมคำสั่งที่ใช้ในการสร้างตารางและเพิ่มข้อมูล
    private static string PrepareScript(DataTable DTable)
    {
    // เตรียมคำสั่งในการสร้าง WorkSheet
    sqlInsert.Length = 0;
    strScript.Length = 0;
    
    ////วนค่าเพื่ออ่านค่าคอลัมน์ที่มีในแต่ละตาราง
    for (int i = 0; i < DTable.Columns.Count; i++)
    {
    sqlInsert.Append( "[" + DTable.Columns[i].ColumnName + "],");
    strScript.Append("[" + DTable.Columns[i].ColumnName.Replace("'", "''") + "]");
    
    ////กำหนดชนิดของข้อมูลแต่ละคอลัมน์
    if (DTable.Columns[i].DataType.ToString().ToLower().Contains("int") || DTable.Columns[i].DataType.ToString().ToLower().Contains("decimal"))
    strScript.Append(" double");
    else
    strScript.Append(" text");
    strScript.Append(", ");
    }
    
    sqlInsert.Remove(sqlInsert.Length - 1, 1);
    strScript.Remove(strScript.Length - 2, 1);
    strScript.Append(") ");
    ////ผลที่ได้ : 
    sqlInsert >> [EmployeeID],[Name],[LastName],[Position]
    strScript >> [EmployeeID] text, [Name] text, [LastName] text, [Position] text ) 
    เพื่อนำไปใช้ในการ run คำสั่งการทำงานสร้าง (create table) และเพิ่มข้อมูล (insert)
    
    return strScript.ToString();
    }
    o เมธอดในการสร้างตาราง/WorkSheet ตามคำสั่งที่เตรียมไว้
    private static void CreateXLSheets(DataTable DTable,
    OleDbConnection xlConn, System.String XLSheetName)
    {
    // สร้าง WorkSheet ใหม่
    System.Text.StringBuilder SqlFinalScript = new System.Text.StringBuilder();
    
    OleDbCommand cmdXl = new OleDbCommand();
    try
    {
    SqlFinalScript.Length = 0;
    cmdXl.Connection = xlConn;
    
    ///สร้างคำสั่งในการสร้างตาราง/WorkSheet ขึ้นใหม่ โดยตั้งชื่อตามพารามิเตอร์ที่ได้รับมา
    SqlFinalScript.Append("CREATE TABLE " + XLSheetName + " (");
    SqlFinalScript.Append(strScript.ToString());
    cmdXl.CommandText = SqlFinalScript.ToString();
    cmdXl.ExecuteNonQuery();
    }
    catch (Exception xlSheetExp)
    {
    throw (new Exception("CreateXLSheetException", xlSheetExp.InnerException));
    }
    finally
    {
    cmdXl.Dispose();
    }
    }
    
    private static void StartExport(DataTable DTable, OleDbConnection xlConn)
    {
    Int64 rowNo = 0, xlSheetIndex = 1, TotalNoOfRecords = 0;
    System.String NewXLSheetName = "Sheet";
    System.Text.StringBuilder strInsert = new System.Text.StringBuilder();
    TotalNoOfRecords = DTable.Rows.Count;
    OleDbCommand cmdXl = new OleDbCommand();
    cmdXl.Connection = xlConn;
    for (int count = 0; count < DTable.Rows.Count; count++)
    {
    strInsert.Length = 0;
    ////กรณีที่เป็นแถวแรกจะทำการสร้าง WorkSheet ขึ้นใหม่ โดยเรียกใช้เมธอด CreateXLSheets
    if (rowNo == 0 )
    { CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
    }
    rowNo += 1;
    
    ////กรณีที่จำนวนแถวใน 1 WorkSheet เกินที่กำหนดไว้จะสร้างชีทใหม่ ในกรณีนี้คือจะสร้าง WorkSheet ใหม่ทุกๆ 3 แถว และเพิ่มลำดับของ index ของ WorkSheet คราวละ 1
    if (TotalNoOfRecords > 3 && rowNo > 3
    {
    xlSheetIndex += 1;
    CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
    rowNo = 1;
    }
    
    ////เขียนคำสั่งในการเพิ่มข้อมูลไปยัง WorkSheet ที่กำหนด โดยมีการแทนที่ค่าของคำสั่งที่เตรียมไว้แล้วก่อนหน้านี้ในตอนเรียกใช้เมธอด PrepareScript
    strInsert.Append("Insert Into [" + NewXLSheetName + xlSheetIndex.ToString() +  "$](" + sqlInsert.ToString() + ") Values (");
    
    
    foreach (DataColumn dCol in DTable.Columns)
    {
    if (dCol.DataType.ToString().ToLower().Contains("int"))
    {
    if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
    strInsert.Append("NULL");
    else
    strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
    }
    else if (dCol.DataType.ToString().ToLower().ToLower().Contains("decimal"))
    {
    if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
    strInsert.Append("NULL");
    else
    strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
    }
    else
    strInsert.Append("\"" +
    DTable.Rows[count][dCol.Ordinal].ToString().Replace("'",
    "''") + "\"");
    strInsert.Append(",");
    }
    strInsert.Remove(strInsert.Length - 1, 1);
    strInsert.Append(");");
    
    ////run คำสั่งที่สร้างขึ้นในการเพิ่มข้อมูลทีละรายการ
    cmdXl.CommandText = strInsert.ToString();
    cmdXl.ExecuteNonQuery();
    
    }
    }
    
    o เขียนการทำงานเมื่อกดปุ่ม “Export”
    protected void btnExport_Click(object sender, EventArgs e)
    { 
     string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), Guid.NewGuid().ToString() + ".xls");
    //เรียกใช้เมธอดในการ Export ข้อมูลแบบ Multiple sheet
    ExportToMultipleXLSheets(fileName);
    
    }

    หลักการทำงานคร่าวๆในการ Export ข้อมูลใน 2 ลักษณะจะมีหลักการพื้นฐานคล้ายกัน ซึ่งจะสรุปได้ดังนี้

    • ดึงข้อมูลจากฐานข้อมูลมาเก็บไว้ใน Datadatable หรือ Dataset
    • กำหนดชื่อไฟล์ที่จะใช้ในการเชื่อมต่อกับ OleDb
    • เชื่อมต่อกับ OleDb โดยการกำหนดค่าต่างๆ เพื่อใช้ในการทำงานกับไฟล์ Excel นั้นๆ
    • เตรียมกำหนดคำสั่ง sql command ในการสร้างโครงสร้างตาราง(create table) และ เพิ่มข้อมูล (insert) ในตารางที่สร้างไว้ หากเป็นกรณีที่ต้องการสร้างแบบ Multiple sheet
      ก็จะทำการเตรียมชุดคำสั่งไว้ และนำไปวน run คำสั่งนั้นๆตามจำนวนรอบที่มีการสร้าง WorkSheet ใหม่นั่นเอง
    • สั่ง run คำสั่งดังกล่าว หากจำนวนแถวของข้อมูลเกินกว่าที่กำหนดต่อ 1 WorkSheet (ซึ่งในกรณีสมมติให้เป็น 3 แถว) จะทำการสร้าง WorkSheet ใหม่
    • อ่านค่าที่ได้จากไฟล์ที่เชื่อมต่อกับ OleDb และผ่านกระบวนการ Export ข้อมูล  มาเขียนเป็นไฟล์ Excel เพื่อให้ผู้ใช้สามารถบันทึกข้อมูลในรูปแบบไฟล์ที่ Export ได้
    • หากเป็นการ Export ข้อมูลแบบ Multiple sheet จำนวนของ WorkSheet จะขึ้นอยู่กับข้อมูลที่ดึงมาและจำนวนที่กำหนดไว้ต่อ 1 WorkSheet นั่นเอง
    หมายเหตุ : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
                –  System.IO
                –  System.Data.OleDb
                –  System.Data
                –  System.Text

                บทความนี้ถือเป็นเพียงการแนะนำวิธีเบื้องต้นในการ Export ข้อมูลไฟล์ในรูปแบบ Excel เท่านั้น ซึ่งผู้พัฒนาสามารถนำความรู้หรือข้อมูลที่ได้จากบทความนี้ไปประยุกต์ ดัดแปลงหรือเพิ่มลูกเล่นให้กับงานที่ท่านกำลังพัฒนาอยู่ได้ เพื่อเพิ่มความสามารถในกระบวนการนำ-เข้าส่งออกดังกล่าว นอกจากนี้ผู้เขียนหวังเป็นอย่างยิ่งว่าบทความนี้อาจเป็นอีกหนึ่งทางเลือกสำหรับผู้พัฒนามือใหม่หรือผู้ที่กำลังค้นหาวิธีการนี้อยู่ สามารถนำไปพัฒนาต่อได้โดยง่าย หากผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ค่ะ

    แหล่งข้อมูลอ้างอิง :
    http://www.codeproject.com/Articles/33271/Import-and-Export-to-Multiple-Worksheets
    http://dotnetawesome.blogspot.com/2013/11/how-to-import-export-database-data-from_18.html

  • ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C#

          การพัฒนาเว็บไซต์ในปัจจุบัน พบว่ามีบางเว็บไซต์มีความต้องการในการแสดงผลตำแหน่ง ที่ตั้งบนแผนที่ Google map เพื่ออำนวยความสะดวกให้กับผู้เยี่ยมชมเว็บไซต์ในการค้นหาตำแหน่งที่ตั้งของสถานที่นั้นๆมากกว่าการบอกเพียงที่อยู่อย่างเช่นแต่ก่อน อาธิเช่น เว็บไซต์ที่เป็นศูนย์รวมในการจองที่พัก ที่มีความจำเป็นต้องแสดงที่ตั้งของโรงแรมที่มีในบริเวณหรือละแวกนั้นๆที่เข้ามาร่วมให้ข้อมูลกับเว็บไซต์ในการจองที่พัก หรือแม้กระทั่งโรงพยาบาล ห้างสรรพสินค้า โรงเรียน มหาวิทยาลัย ที่เว็บไซต์ต้องการแสดงที่ตั้งเพื่อให้ผู้เยี่ยมชมเว็บไซต์สามารถทราบได้ว่าสถานที่เหล่านั้นมีที่ตั้งอยู่ในบริเวณใดบ้าง เพื่อเป็นประโยชน์ให้ผู้ที่เข้ามาเยี่ยมชมเว็บไซต์สามารถเรียกดูได้จากแผนที่เพื่อศึกษาเส้นทาง หรือหาตำแหน่งที่จะสามารถไปยังจุดนั้นๆได้โดยง่ายและใช้ระยะทางใกล้ที่สุดนั่นเอง
          ในบทความนี้ ผู้เขียนจึงขอพูดถึงวิธีการแสดงผลตำแหน่งที่ตั้งบน Google map ซึ่งมีทั้งแบบกำหนดตายตัว โดยมีการระบุตำแหน่งที่ตั้งทั้งละติจูดและลองจิจูด และแบบที่มีการดึงค่าของละติจูดและลองจิจูดมาจากฐานข้อมูลของเว็บไซต์ที่พัฒนาโดยใช้เครื่องมือ ASP.NET ด้วย C# และแบบที่มีการกำหนดจุดแสดงตำแหน่งเพียงจุดเดียวและหลายจุดพร้อมกัน เพื่อประโยชน์กับนักพัฒนาท่านอื่นๆที่มีความสนใจสามารถนำไปประยุกต์ใช้กับเว็บไซต์ของตนได้

          โดยผู้เขียนขอเสนอวิธีการเบื้องต้นในการแสดงผลแบบกำหนดค่าตายตัวให้ผู้อ่านลองศึกษาการทำงานเพื่อทำความเข้าใจในเบื้องต้นก่อน ดังนี้

    การแสดงผลแบบจุดเดียว

    1. อ้างอิงพาธที่ตั้งของ Google API ซึ่งเป็นส่วนหนึ่งของการแสดงผลบนแผนที่ Google map และไฟล์จาวาสคริปต์ที่ใช้ในการแสดงผล(ถ้ามี)
    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
     
    <script src="js/mapwithmarker.js" type="text/javascript"></script>
    1. กำหนดสไตล์ชีทที่ใช้ในการแสดงผล เมื่อมีการคลิกตำแหน่งที่ได้ทำการกำหนดพิกัดไว้
    <style type="text/css">  
    .labels { color: black; background-color: #FF8075; font-family: Arial; font-size: 11px; font-weight: bold; text-align: center; width: 12px; }  </style>
    1. กำหนดพิกัดที่ต้องการให้แผนที่ค้นหาจุดกึ่งกลางของการแสดงผล ซึ่งโดยปกติจะถือเอาจุดแรกที่ต้องการแสดงเป็นตำแหน่งกึ่งกลางของการแสดงผลตำแหน่งบนแผนที่นั้นๆ เพื่อให้ตำแหน่งดังกล่าวอยู่กึ่งกลางของแผนที่ที่ต้องการแสดงนั่นเอง
    var mapOptions = {  
    center: new google.maps.LatLng(ค่าละติจูด, ค่าลองจิจูด),  
    zoom: 12, 
    ///ขนาดที่ต้องการให้ซูมเป็นค่าตั้งต้น  
    mapTypeId: google.maps.MapTypeId.ROADMAP  };
    1. กำหนดส่วนที่ต้องการให้แสดงแผนที่ ว่าต้องการให้แสดงในส่วนใดของเว็บไซต์
    var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
     ///ในที่นี้พื้นที่ที่ต้องการให้แสดงผลในเว็บไซต์ คือ dvMap โดยนำค่าที่กำหนดกึ่งกลางไว้ในขั้นตอนที่ 3 (mapOptions) มาเป็นค่าพารามิเตอร์ในการแสดงผลด้วย
    
    1. การกำหนดจุดพิกัดที่ต้องการแสดงผล ซึ่งค่าที่ต้องการคือ ชื่อสถานที่ ค่าละติจูด ลองจิจูด และคำอธิบายในการแสดงผลตำแหน่งสถานที่ที่เราทำการกำหนดไว้ ดังนี้
      • กำหนดค่าพิกัดลองจิจูดและละติจูดของจุดที่เราต้องการกำหนดบนแผนที่
      var infoWindow = new google.maps.InfoWindow();
       var myLatlng = new google.maps.LatLng(ค่าละติจูด, ค่าลองจิจูด);
      
      
      • กำหนดค่าพิกัดตำแหน่งของจุดและพารามิเตอร์ต่างๆที่จำเป็นต้องใช้ในการกำหนดจุดที่เราต้องการกำหนดบนแผนที่
      var marker = new MarkerWithLabel({
       position: myLatlng, //เป็นการกำหนดค่าพิกัดตำแหน่งของจุดที่เราต้องการกำหนดบนแผนที่
       map: map, //เป็นการกำหนดพื้นที่ที่ต้องการแสดงแผนที่ ในที่นี้คือ dvMap
       title: title, //เป็นการกำหนดชื่อสถานที่
       labelContent:1,  //เป็นการกำหนดหมายเลขลำดับของตำแหน่งแสดงผล
       labelAnchor: new google.maps.Point(7, 30),
       labelClass: "labels", //เป็นการกำหนดรูปแบบในการแสดงผลด้วยสไตล์ชีท
       labelInBackground: false
       });
      
      
      • กำหนดการแสดงผลเมื่อผู้เยี่ยมชมมีการคลิกบนจุดดังกล่าว
      (function(marker) {
       google.maps.event.addListener(marker, "click", function(e) {
       infoWindow.setContent(description);
       //เป็นการกำหนดข้อความที่ต้องการแสดง เมื่อผู้เยี่ยมชมเว็บไซต์คลิกบนจุดดังกล่าว
       infoWindow.open(map, marker);
       });
       })(marker);
      
      
    1. สร้างพื้นที่ที่กำหนดการแสดงผลในส่วน body
    <div id="dvMap" style="width: 800px; height: 700px;">
     </div>
    1. แสดง code ทั้งหมดที่ใช้
    <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
     <!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 runat="server">
     <title>Google map Test for one point</title>
     <style type="text/css">
     .labels { color: black; background-color: #FF8075; font-family: Arial; font-size: 11px; font-weight: bold; text-align: center; width: 12px; }
     </style>
     <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
     <script src="js/mapwithmarker.js" type="text/javascript"></script>
     <script type="text/javascript">
     window.onload = function() {
     var mapOptions = {
     center: new google.maps.LatLng(7.006923, 100.500238),
     zoom: 12,
     mapTypeId: google.maps.MapTypeId.ROADMAP
     };
     var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
     var infoWindow = new google.maps.InfoWindow();
     var myLatlng = new google.maps.LatLng(7.006923, 100.500238);
     var marker = new MarkerWithLabel({
     position: myLatlng,
     map: map,
     title: 'มหาวิทยาลัยสงขลานครินทร์',
     labelContent: 1,
     labelAnchor: new google.maps.Point(7, 30),
     labelClass: "labels", // the CSS class for the label
     labelInBackground: false
     });
     (function(marker ) {
     google.maps.event.addListener(marker, "click", function(e) {
     infoWindow.setContent('มหาวิทยาลัยสงขลานครินทร์ วิทยาเขตหาดใหญ่');
     infoWindow.open(map, marker);
     });
     })(marker );
     }
     </script>
     </head>
     <body>
     <form id="form1" runat="server">
     <div id="dvMap" style="width: 800px; height: 700px;">
     </div>
     </form>
     </body>
     </html>

    ตัวอย่างภาพผลลัพธ์ที่ได้ :
    Map1point

    แบบหลายจุดและดึงค่าละติจูด ลองจิจูดจากฐานข้อมูลมาแสดงผล

    โดยลักษณะการเขียนจะคล้ายๆกับแบบกำหนดจุดเพียงจุดเดียวอย่างที่กล่าวไว้แล้วในตอนต้น แต่จะแตกต่างกันในส่วนของการดึงข้อมูล ซึ่งจะเน้นเฉพาะส่วนที่แตกต่าง และแสดง code สรุปรวมให้ดูอีกครั้ง

    ไฟล์ default.aspx ในฝั่ง Client

    1. กำหนดค่าตัวแปรที่ใช้ในแสดงผลจาก Repeater ซึ่งเป็นเครื่องมือที่มีอยู่แล้วใน .NET โดยตัวแปรที่จะใช้ที่นี้ให้มีชื่อว่า markers และค่าที่ต้องการนำมาใช้จะเป็นฟิลด์ของ Name, Latitude, Longitude และ Description ซึ่งดึงมาจากฐานข้อมูล(โดยชื่อฟิลด์สามารถเปลี่ยนแปลงได้ตามข้อมูลที่มีอยู่จริงในฐานข้อมูลของแต่ละท่าน)
      <script type="text/javascript">
       var markers = [
       <asp:Repeater ID="rptMarkers" runat="server">
       <ItemTemplate>
       {
       "title": '<%# Eval("Name") %>',
       "lat": '<%# Eval("Latitude") %>',
       "lng": '<%# Eval("Longitude") %>',
       "description": '<%# Eval("Description") %>'
       }
       </ItemTemplate>
       <SeparatorTemplate>
       ,
       </SeparatorTemplate>
       </asp:Repeater>
       ];
       </script>
    2. กำหนดพิกัดที่ต้องการให้แผนที่ค้นหาจุดกึ่งกลางของการแสดงผล ซึ่งโดยปกติจะถือเอาจุดแรกที่ต้องการแสดงเป็นตำแหน่งกึ่งกลางของการแสดงผลตำแหน่งบนแผนที่นั้นๆ เพื่อให้ตำแหน่งดังกล่าวอยู่กึ่งกลางของแผนที่ที่ต้องการแสดงนั่นเอง
      var mapOptions = {
       center: new google.maps.LatLng(markers[0].lat, markers[0].lng),
       zoom: 8,
       mapTypeId: google.maps.MapTypeId.ROADMAP};
    3. กำหนดค่าต่างๆ และวนค่าตัวแปร markers ที่ดึงมาจากฐานข้อมูลและตัว Repeater ที่กล่าวไว้ในข้างต้น และทำการกำหนดค่าต่างๆให้กับแต่ละจุดที่ต้องการ โดยรายละเอียดในการกำหนดค่าจะคล้ายกับวิธีกำหนดแบบจุดเดียว(แบบแรก) แต่จะมีการวนซ้ำให้ครบจำนวนตัวแปรที่ดึงมาจากฐานข้อมูล
      var infoWindow = new google.maps.InfoWindow();
       var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
       for (i = 0; i < markers.length; i++) {
       //เป็นการวนค่าตัวแปร markers ที่ดึงมาจากฐานข้อมูลและตัว Repeater ที่กล่าวไว้ในข้างต้น
      
        var data = markers[i]
       //นำค่าจากตัวแปร markers  ที่ดึงมาทีละรายการตามลำดับมาใส่ไว้ในตัวแปร data เพื่อนำไปใช้ในการกำหนดค่าต่อไป
      var myLatlng = new google.maps.LatLng(data.lat, data.lng);
       //กำหนดค่าละติจูดและลองจิจูด
      var marker = new MarkerWithLabel({
       position: myLatlng, //เป็นการกำหนดตำแหน่งที่ต้องการแสดงผล
      map: map,
       title: data.title, //เป็นการกำหนดชื่อที่ต้องการแสดงผลเมื่อนำเมาส์ไปชี้ยังจุดดังกล่าว
      labelContent: i+1//เป็นการกำหนดหมายเลขลำดับให้กับจุดดังกล่าว
       labelAnchor: new google.maps.Point(7, 30),
       labelClass: "labels", // the CSS class for the label
       labelInBackground: false}
       );(function (marker, data) {google.maps.event.addListener(marker, "click", function (e) {
       infoWindow.setContent(data.description);
       //เป็นการกำหนดข้อความที่ต้องการแสดง เมื่อผู้เยี่ยมชมเว็บไซต์คลิกบนจุดดังกล่าว
      infoWindow.open(map, marker);
       });
       })(marker, data);
       }
    4. Code ทั้งหมดในฝั่งไฟล์ Default.aspx
      <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
      
      <!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 runat="server">
       <title></title>
       </head>
       <body>
       <form id="form1" runat="server">
       <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script><script src="js/mapwithmarker.js" type="text/javascript"></script>
      <script type="text/javascript">
       var markers = [
       <asp:Repeater ID="rptMarkers" runat="server">
       <ItemTemplate>
       {
       "title": '<%# Eval("Name") %>',
       "lat": '<%# Eval("Latitude") %>',
       "lng": '<%# Eval("Longitude") %>',
       "description": '<%# Eval("Description") %>'
       }
       </ItemTemplate>
       <SeparatorTemplate>
       ,
       </SeparatorTemplate>
       </asp:Repeater>
       ];
       </script>
       <script type="text/javascript">
       window.onload = function() {
       var mapOptions = {
       center: new google.maps.LatLng(markers[0].lat, markers[0].lng),
       zoom: 8,
       mapTypeId: google.maps.MapTypeId.ROADMAP
       };
       var infoWindow = new google.maps.InfoWindow();
       var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
       var j = 1;
       for (i = 0 ; i <= markers.length-1; i++) {
       var data = markers[i]
       var myLatlng = new google.maps.LatLng(data.lat, data.lng);
       // var marker = new google.maps.Marker({
       var marker = new MarkerWithLabel({
       position: myLatlng,
       map: map,
       title: data.title,
       labelContent: i+1,
       labelAnchor: new google.maps.Point(7, 30),
       labelClass: "labels", // the CSS class for the label
       labelInBackground: false
       });
       (function(marker, data) {
       google.maps.event.addListener(marker, "click", function(e) {
       infoWindow.setContent(data.description);
       infoWindow.open(map, marker);
       });
       })(marker, data);
       }
       }
       </script>
       <div id="dvMap" style="width: 500px; height: 500px">
       </div>
       </form>
       </body>
       </html>

     

    ไฟล์ default.cs ในฝั่ง server

    using System;
     using System.Collections.Generic;
     using System.Web;
     using System.Web.UI;
     using System.Web.UI.WebControls;
     using System.Data;public partial class _Default : System.Web.UI.Page
     {
     protected void Page_Load(object sender, EventArgs e)
     {
     if (!this.IsPostBack)
     {
     rptMarkers.DataSource = GetData();
    //กำหนดแหล่งข้อมูลให้กับตัว Repeater ที่เราต้องการใข้ในการแสดงผล
     rptMarkers.DataBind();
     }
     }
      private DataTable GetData()
    //เมธอดที่ใช้ในการดึงค่าข้อมูลจากฐานข้อมูล ซึ่งในที่นี้มีการส่งค่ากลับเป็น datatable และมีการสมมุติค่าข้อมูลใน datatable เพื่อให้เห็นภาพการดึงจากฐานข้อมูลเท่านั้น แต่สามารถนำไปประยุกต์ใช้กับการติดต่อฐานข้อมูลจริงได้  {
     DataTable table = new DataTable();
     table.Columns.Add("Name", typeof(string));
     table.Columns.Add("Latitude", typeof(decimal));
     table.Columns.Add("Longitude", typeof(decimal));
     table.Columns.Add("Description", typeof(string));
     table.Rows.Add("มหาวิทยาลัยสงขลานครินทร์", 7.006923, 100.500238, "Hatyai");
     table.Rows.Add("มหาวิทยาลัยราชภัฏ สงขลา", 7.172661, 100.613726, "Songkla");
     return table;
     }
     }
    1. ดึงข้อมูลจากแหล่งข้อมูลที่ต้องการ ซึ่งประกอบด้วยข้อมูลตามที่ต้องการให้แสดงผล เช่น ค่าละติจูด ลองจิจูด และชื่อสถานที่ โดยในกรณีนี้ผู้เขียนขอสมมติค่าและสร้าง datatable ขึ้นมา เพื่อใช้ในการทดสอบ โดยแสดงในเมธอด GetData() หากเป็นข้อมูลที่ใช้จริงผู้พัฒนาสามารถนำไปประยุกต์กับการดึงข้อมูลของท่านได้
    2. กำหนดค่าแหล่งข้อมูลให้กับ repeater เพื่อสร้างตัวแปร marker ตามที่กล่าวไว้ในข้างต้น
    3. ผลลัพธ์ที่ได้ :

    Mapmultipoint

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

    แหล่งข้อมูลอ้างอิง :
    http://www.dotnetbull.com/2013/06/multiple-marker-with-labels-in-google.html
    http://www.codeproject.com/Articles/36151/Show-Your-Data-on-Google-Map-using-C-and-JavaScrip