บทความ

Hierarchical Data Binding in ASP.NET

เรตติ้ง
เขียนโดย admin เมื่อวันที่ 11 March 2008 ตอน 11:02

ใช้ได้กับ

Microsoft ASP.NET

สรุป:

เรียนรู้เทคนิคการทำให้ ASP.NET เชื่อมโยงข้อมูลไปยังแหล่งข้อมูล โดยที่ข้อมูลจะเป็นแบบแยกสาขามีมากกว่า 2 มิติขึ้นไป

คำนำ

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

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

การเชื่อมโยงข้อมูล

การเชื่อมโยงข้อมูลของ   ASP.NET   เป็นขั้นตอนการเชื่อมโยงข้อมูลในเซิร์ฟเวอร์ไปยังคอนโทรลในฝั่งเซิร์ฟเวอร์ จากนั้นก็จะมีการสร้างภาพผลลัพธ์ข้อมูลแบบใดแบบหนึ่งจากเซิร์ฟเวอร์ส่งไปยังไคล์เอ็นต์ ข้อจำกัดเพียงอย่างเดียวของการเชื่อมโยงข้อมูลก็คือคอนโทรลในฝั่งไคล์เอ็นต์จะต้องรองรับการทำงานของ  property  ที่ชื่อ DataSource และ   method   ที่ชื่อ   DataBind()  รวมทั้งแหล่งข้อมูลที่คอนโทรลจะเชื่อมโยงไปหาต้องมีการใช้อินเทอร์เฟซ IEnumerable ด้วย

หมายเหตุ:  มีข้อยกเว้น  2  ข้อเกี่ยวกับเงื่อนไขนี้ นั่นก็คือ  DataSet และ DataTable สาามารถเชื่อมโยงกับแหล่งข้อมูลโดยตรง ซึ่งจะส่งผลทำให้มีการเชื่อมโยงไปยัง  DataView ปกติของตารางปกติ (DataView มีการใช้ IEnumerable  อยู่แล้ว)  วิธีนี้ก่อให้เกิดความสะดวกอย่างมากเนื่องจาก DataSets และ DataTables มักถูกใช้เป็นแหล่งข้อมูลสำหรับการเชื่อมโยงข้อมูลอยู่แล้ว

ถ้าหากต้องการเชื่อมโยงข้อมูลไปหาคอนโทรล คุณต้องกำหนดแหล่งข้อมูลไปยัง  DataSource property ของคอนโทรล แล้วเรียก DataBind() method

ตัวอย่างเช่น ถ้าหากเป็นแหล่งข้อมูลนี้ก็จะมีการส่งข้อมูล ArrayList ในคลาส Item กลับมา

public class Item
  {
    private string _name;
    public Item(string name) { _name = name; }
    public string Name { get { return _name; } }
  }
  public class TestDataSource
  {
    public static ArrayList GetData()
    {
      ArrayList items = new ArrayList();
      for (int i=0; i<10; i++)
      {
        Item item = new Item("item" + i.ToString());
        items.Add(item);
      }
      return items;
    }
  }

การที่  ArrayList  มีการใช้  IEnumerable  อยู่แล้ว ผลลัพธ์ของ GetData() method ของเราที่อยู่ในคลาส TestDataSource  จึงเป็นแหล่งข้อมูลที่เหมาะสมสำหรับการเชื่อมโยงข้อมูลได้ เราจะใช้  Repeater  เป็นคอนโทรลในฝั่งเซิร์ฟเวอร์ ซึ่งเราจะใช้ในการเชื่อมโยงข้อมูล โดยที่เราจำเป็นต้องใส่ ItemTemplate เพื่ออธิบายว่าเราจะแสดงผลแหล่งข้อมูลอย่างไร ตัวอย่างของเราแสดงผลคอนโทรล   CheckBox  โดยใช้ข้อความที่กำหนดให้ Name property ของ Item class instance ที่เชื่อมโยงกันอยู่

<asp:Repeater Runat="server" ID="_itemsRepeater" 
              EnableViewState="false">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
 Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'
  />
    <br/>
  </ItemTemplate>
</asp:Repeater>

งานที่เหลือก็คือการเชื่อมโยงข้อมูลจริงๆไปยัง  Repeater  ของเรา ซึ่งโดยปกติแล้วมักจะทำในช่วงการจัดการการ Load ของคลาสที่ได้มาจาก Page ซึ่งแสดงเอาไว้ด้านล่างนี้

    private void Page_Load(object sender, EventArgs e)
   {
      _itemsRepeater.DataSource = TestDataSource.GetData();
      _itemsRepeater.DataBind();
    }

ตัวอย่างของการแสดงผลเพจแสดงเอาไว้ด้านล่างนี้

 

ภาพที่ 1: การเชื่อมโยงข้อมูลเพจ

 

ข้อมูลแบบแยกสาขา

ตัวอย่างแหล่งข้อมูลชนิดแรกของเราเป็นข้อมูลแบบแบนราบที่มีข้อมูลเพียงระดับเดียว ในตอนนี้สมมติว่าเราเพิ่มชุด item ย่อยให้แก่แต่ละ item ที่อยู่ในแหล่งข้อมูลของเราตามตัวอย่างนี้

public class Item
{
  string _name;
  ArrayList _subItems = new ArrayList();
  public Item(string name) { _name = name; }
  public string    Name     { get { return _name;     } }
  public ArrayList SubItems { get { return _subItems; } }
}

จากนั้นเพิ่ม  method  ที่ชื่อ  GetData ลงไปในแหล่งข้อมูลของเรา โดยเราจะเพิ่ม item ย่อย 5 ชุดให้แก่แต่ละ item

public class TestDataSource
{
  public static ArrayList GetData()
  {
    ArrayList items = new ArrayList();
    for (int i=0; i<10; i++)
    {
      Item item = new Item("item" + i.ToString());
      for (int j=0; j<5; j++)
      {
        Item subItem = new Item("subitem" + j.ToString());
        item.SubItems.Add(subItem);
      }
      items.Add(item);
    }
    return items;
  }
}

ในตอนนี้โครงสร้างข้อมูลของเราเป็นแบบแยกสาขา โดยมีข้อมูลอยู่ต่ำกว่าระดับบนสุดอีกหนึ่งชุด การเชื่อมโยงข้อมูลที่เราทำเอาไว้ก่อนหน้านี้ยังคงทำงานได้อย่างถูกต้อง แต่มันจะแสดงข้อมูลในระดับแรกเท่านั้น โดยไม่สนใจ  item ย่อยในขณะแสดงผล สิ่งที่เราต้องการตรงจุดนี้เพื่อทำให้การแสดงผลข้อมูลทั้งหมดถูกต้องก็คือการเชื่อมโยงข้อมูลเชิงซ้อนของ item ย่อยที่อยู่ใน item หลัก โดยปกติแล้วสิ่งนี้หมายความว่าเราต้องใส่คอนโทรลเชื่อมโยงข้อมูลอีกอันหนึ่งลงไปภายใน  ItemTemplate  ของ Repeater ที่เรามีอยู่แล้ว จากนั้นทำการเชื่อมโยงไปหาชุด SubItems ของแต่ละ  Item  ที่กำหนดเอาไว้ใน Repeater ระดับบนสุด เราสามารถกำหนดองค์ประกอบดังกล่าวได้ภายในไฟล์ .aspx  ของเราโดยการเพิ่ม  Repeater  เชิงซ้อนลงไป ส่วนที่ค่อนข้างยุ่งยากก็คือการเทียบชุด SubItems ของ Item  ที่กำลังมีการเชื่อมโยงไปหา  DataSource  property ของ Repeater เชิงซ้อนอยู่ สิ่งที่เราต้องทำก็คือการกำหนด  DataSource  property ของ Repeater เชิงซ้อนให้แก่ data binding expression ซึ่งจะส่งผลต่อชุด SubItems เหมือนอย่างที่แสดงเอาไว้ด้านล่างนี้

<asp:Repeater Runat="server" ID="_itemsRepeater" 
              EnableViewState="false">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
    Text='<%# DataBinder.Eval(Container.DataItem, "Name") 
      %>' 
         />
    <asp:Repeater Runat="server" ID="_subitemsRepeater"
                  EnableViewState="false"
         DataSource=
   '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'>
         <ItemTemplate>
           <br/>&nbsp;&nbsp;&nbsp;
           <asp:CheckBox Runat="server"
                         Text=
      '<%# DataBinder.Eval(Container.DataItem, "Name") %>' 
        />
         </ItemTemplate>
    </asp:Repeater>

    <br/>
  </ItemTemplate>
</asp:Repeater>

ไม่มีอะไรเปลี่ยนแปลงเกิดขึ้นกับคลาสที่อยู่เบื้องหลังโค้ด เนื่องจากเราได้เชื่อมโยงแหล่งข้อมูลของเรากับ Repeater  ระดับบนสุดอยู่แล้ว การเชื่อมโยงข้อมูลเชิงซ้อนจะเกิดขึ้นเพียงครั้งเดียวต่อ Item ซึ่งอยู่ในชุดข้อมูลระดับบนสุด สิ่งสำคัญที่ต้องจำไว้ก็คือเมื่อคุณอ่านคอนโทรลเชื่อมโยงข้อมูลเชิงซ้อนหนึ่งคู่แบบนี้     data-binding expression  (<%# %>) จะถูกกำหนดขอบเขตโดยคอนโทรลที่อยู่ใกล้ที่สุด ซึ่งในตัวอย่างของเรา data-binding expression  สองชุดแรก ถูกกำหนดขอบเขตโดยการเชื่อมโยงข้อมูลภายนอกของ Repeater ระดับบนสุด และแยกไปยัง  Item  ปัจจุบันในชุดข้อมูลระดับบนสุด ส่วน data-binding expression ชุดที่สามถูกกำหนดขอบเขตโดย Repeater  ภายใน และแยกไปยังองค์ประกอบในชุด  SubItems ของ Item ปัจจุบันที่เชื่อมโยงอยู่ ตัวอย่างการแสดงผลเพจนี้แสดงอยู่ด้านล่าง

>

ภาพที่ 2: การแสดงผลเพจที่มีการเชื่อมโยงไปยัง Repeater

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

public class TestDataSource
{
  public static ArrayList GetData()
  {
    ArrayList items = new ArrayList();
    for (int i=0; i<10; i++)
    {
      Item item = new Item("item" + i.ToString());
      for (int j=0; j<5; j++)
      {
        Item subItem = new Item("subitem" + j.ToString());
        item.SubItems.Add(subItem);
        for (int k=0; k<4; k++)
        {
          Item subsubItem = 
               new Item("subsubitem" + k.ToString());
          subItem.SubItems.Add(subsubItem);
        }
      }
      items.Add(item);
    }
    return items;
  }
}

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

<asp:Repeater Runat="server" ID="_itemsRepeater"
              EnableViewState="false">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
 Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'
   />
    <asp:Repeater Runat="server" ID="_subitemsRepeater"
         EnableViewState="false"
         DataSource=
    '<%# DataBinder.Eval(Container.DataItem, "SubItems") %>'
      >
      <ItemTemplate>
        <br/>&nbsp;&nbsp;&nbsp;
        <asp:CheckBox Runat="server"
             Text=
       '<%# DataBinder.Eval(Container.DataItem, "Name") 
         %>'/>
        <asp:Repeater Runat="server" EnableViewState="false"
             DataSource=
    '<%# DataBinder.Eval(Container.DataItem, "SubItems") 
      %>'>
          <ItemTemplate>
            <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <asp:CheckBox Runat="server"
                          Text=
      '<%# DataBinder.Eval(Container.DataItem, "Name") %>' 
        />
          </ItemTemplate>
        </asp:Repeater>
      </ItemTemplate>
    </asp:Repeater>
    <br />
  </ItemTemplate>
</asp:Repeater>

การแสดงผลบางส่วนของเพจอยู่ด้านล่างนี้

 

ภาพที่ 3 เพจที่แสดง Repeater เชิงซ้อนอีกชุดหนึ่ง

 

การเชื่อมโยงไปยังข้อมูลดาต้าเบสแยกสาขา

ในตอนนี้เมื่อเรามีความรู้พื้นฐานเกี่ยวกับการเชื่อมโยงข้อมูลแยกสาขาแล้ว เราควรจะมาทดสอบกับแอพพลิเคชันที่เหมือนกับการใช้งานจริงมากขึ้นจะดีกว่า การที่การเชื่อมโยงข้อมูลส่วนใหญ่เกี่ยวข้องกับผลัพธ์ของการคิวรีดาต้าเบส ดังนั้นลำดับต่อไปเราจะดึงข้อมูลแยกสาขามาจากดาต้าเบสกัน โดยปกติแล้วข้อมูลแยกสาขาจะเก็บเอาไว้ในดาต้าเบสแบบรีเลชันแนลโดยการใช้ความสัมพันธ์หนึ่งต่อจำนวนมากระหว่างตารางต่างๆ ตัวอย่างเช่นในดาต้าเบสตัวอย่างที่ชื่อ Northwind  ที่มีอยู่ใน  SQL Server และ Microsoft Access นั้น เราจะพบความสัมพันธ์หนึ่งต่อจำนวนมากระหว่างตาราง  Customer  และตาราง  Orders สิ่งที่คล้ายคลึงกันก็คือ มีความสัมพันธ์หนึ่งต่อจำนวนมากระหว่างตาราง Orders และตาราง Order Details ด้วย ความสัมพันธ์ดังกล่าวแสดงเอาไว้ในภาพด้านล่างนี้

 

ภาพที่ 4 ความสัมพันธ์ของตาราง

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

private void Page_Load(object sender, EventArgs e)
{
  string strConn = 
       "server=.;trusted_connection=yes;database=northwind";

  string strSql  = "SELECT CustomerID, CompanyName FROM " +
                   " Customers; "                         +
                   "SELECT OrderID, CustomerID, "         + 
                   "        EmployeeID FROM Orders;"      +
                   "SELECT OrderID, Products.ProductID,"  +
                   "ProductName, Products.UnitPrice FROM" +
                   " [Order Details], Products WHERE "    +
                   " [Order Details].ProductID = "        +
                   "Products.ProductID";

  SqlConnection conn = new SqlConnection(strConn);
  SqlDataAdapter da = new SqlDataAdapter(strSql, conn);
  da.TableMappings.Add("Customers1", "Orders");
  da.TableMappings.Add("Customers2", "OrderDetails");

  _ds = new DataSet();

  da.Fill(_ds, "Customers");

  _ds.Relations.Add("Customer_Order", 
        _ds.Tables["Customers"].Columns["CustomerID"], 
        _ds.Tables["Orders"].Columns["CustomerID"]);
  _ds.Relations[0].Nested = true;
  _ds.Relations.Add("Order_OrderDetail", 
        _ds.Tables["Orders"].Columns["OrderID"], 
        _ds.Tables["OrderDetails"].Columns["OrderID"]);
  _ds.Relations[1].Nested = true;

  _customerRepeater.DataSource = _ds.Tables["Customers"];
  _customerRepeater.DataBind();
}

หลังจากที่ข้อมูลถูกโหลดเข้ามาสู่  DataSet  แล้ว ในตอนนี้เราจะสามารถค้นหาข้อมูลแยกสาขาได้ โดยการใช้ความสัมพันธ์ที่เรากำหนดขึ่นมา ตัวอย่างเช่นเมื่อเลือกแถวใดแถวหนึ่งในตาราง Customers ที่อยู่ภายใน DataSet เราสามารถเรียก  GetChildRows() พร้อมกับสตริง "Customer_Order" เพื่อดึงชุดของแถวจากตาราง Orders ที่เกี่ยวข้องกับลูกค้าคนนั้นออกมาได้ หรือขั้นตอนที่คล้ายคลึงกันก็คือเราสามารถค้นหารายการ  Order  Details ทั้งหมดที่เกี่ยวข้องกับรายการสั่งซื้อบางรายได้ โดยการเรียก       GetChildRows       พร้อมกับสตริง "Order_OrderDetail"  ในแถวใดแถวหนึ่งจากตาราง Orders เพื่อดึงรายการ Order Detail ทั้งหมดที่เกี่ยวข้องกับรายการสั่งซื้อนั้นขึ้นมา ถ้าหากดูจากจุดมุ่งหมายของเราแล้ว สิ่งที่มีประโยชน์มากขึ้นก็คือ CreateChildView method ของ DataRowView class ที่ส่ง DataView ซึ่งแสดงแถวทั้งหมดของความสัมพันธ์ดังกล่าวมาให้

ในตอนนี้เมื่อเรามีข้อมูลที่เราเตรียมสำหรับการเชื่อมโยงข้อมูลแล้ว เราจำเป็นต้องเพิ่มคอนโทรลเชื่อมโยงข้อมูลกับข้อมูลเชิงซ้อนแต่ละระดับอย่างเหมาะสมสำหรับการแสดงผลข้อมูล สิ่งที่หมือนกับตัวอย่างก่อนหน้านี้ที่ใช้โครงสร้างข้อมูลพิเศษก็คือ ข้อมูลที่เรากำลังทำการเชื่อมโยงก็มีความลึก  2  ระดับ ซึ่งนั่นหมายความว่าเราต้องการคอนโทรลเชิงซ้อนสองอันเพื่อแสดงผลข้อมูลย่อยแต่ละระดับ ถ้าหากพูดให้เจาะจงลงไปเราต้องการ Repeater ระดับบนสุดหนึ่งอัน เพื่อเชื่อมโยงไปหาตาราง  Customers ใน DataSet ต้องการ Repeader เชิงซ้อนอีกหนึ่งตัวเพื่อเชื่อมโยงไปยัง Orders  ทั้งหมดที่เกี่ยวข้องกับลูกค้าแต่ละราย และต้องการ Repeater เชิงซ้อนอีกหนึ่งอันเพื่อเชื่อมโยงไปยังรายการ  Order Detail ที่เกี่ยวข้องกับรายการสั่งซื้อแต่ละรายการ ส่วน DataSource ของ Repeater เชิงซ้อนทั้งสองอันจะเป็นผลลัพธ์ของการเรียก  CreateChildView  ในแถว  parent  โดยการระบุชื่อความสัมพันธ์ที่เหมาะสมลงไป ดังนั้นแทนที่จะพยายามสร้าง  DataView  ใน  expression  เพียงอันเดียว ที่อยู่ภายใน Repeater declaration วิธีการที่สะดวกกว่าก็คือการกำหนดฟังก์ชันเอาไว้ในโค้ดที่อยู่เบื้องหลังคลาสที่รับแถว parent และชื่อของความสัมพันธ์เข้ามา จากนั้นส่งกลับไปยัง DataView

protected DataView GetChildRelation(object dataItem,
                                  string relation)
{
  DataRowView drv = dataItem as DataRowView;
  if (drv != null)
    return drv.CreateChildView(relation);
  else
    return null;
}

เมื่อมีฟังก์ชันดังกล่าวและแหล่งข้อมูลพร้อมแล้ว ในตอนนี้เราสามารถเขียน  Repeater control declarations ในไฟล์ .aspx ของเราได้ ซึ่งเป็นตัวแทนของเลย์เอาท์แบบเรียบง่ายที่สุดของข้อมูลที่ใช้ breaks และ spaces

<asp:Repeater Runat="server" ID="_customerRepeater" 
              EnableViewState="false">
  <ItemTemplate>
    Customer:
    <%# DataBinder.Eval(Container.DataItem, "CustomerID") %>
   &nbsp; &nbsp;
    <%# DataBinder.Eval(Container.DataItem,"CompanyName") %>
    <br />
    <asp:Repeater runat="server" EnableViewState="false"
         DataSource=
            '<%# GetChildRelation(Container.DataItem, 
                                  "Customer_Order")%>'
    >
      <itemTemplate>
       &nbsp;&nbsp;&nbsp;&nbsp;
       Orderid:<b>
       <%#DataBinder.Eval(Container.DataItem, "OrderID")%> 
       </b><br/>
       <asp:Repeater runat="server" EnableViewState="false"
            DataSource=
                '<%# GetChildRelation(Container.DataItem, 
                                     "Order_OrderDetail")%>'
       >
         <itemTemplate>
           &nbsp;&nbsp;&nbsp;&nbsp;
           &nbsp;&nbsp;&nbsp;&nbsp;
           <b><%# DataBinder.Eval(Container.DataItem, 
                                  "ProductName") %></b>
           $<%# DataBinder.Eval(Container.DataItem, 
                                "UnitPrice") %> <br/>
         </itemTemplate>
       </asp:Repeater>
     </itemTemplate>
   </asp:Repeater>
 </ItemTemplate>
</asp:Repeater>

การเชื่อมโยงไปหาข้อมูล XML

การพูดถึงข้อมูลแยกสาขาใดๆจะไม่สมบูรณ์แบบ ถ้าหากไม่ได้พูดถึง  XML  รวมลงไปด้วย เนื่องจากมันเป็นฟอร์แมทหลักของข้อมูลแยกสาขาในระบบส่วนใหญ่ของปัจจุบัน แต่การเชื่อมโยงเซิร์ฟเวอร์คอนโทรลไปยังข้อมูล XML ใน ASP.NET  มีทางเลือกไม่มากนัก ทางเลือกอย่างหนึ่งก็คือการอ่านข้อมูล  XML  ไปไว้ใน DataSet จากนั้นก็ใช้เทคนิคที่แสดงเอาไว้ในหัวข้อก่อนหน้านี้ ทางเลือกอีกแบบหนึ่งก็คือการใช้ XML API ใน .NET เพื่อโหลดข้อมูลขึ้นมาโดยตรง จากนั้นก็เชื่อมโยงกับคลาสที่ระบุเอาไว้ในข้อมูลที่โหลดขึ้นมา ทางเลือกสุดท้ายที่น่าจะเป็นทางเลือกที่ดีมากที่สุดก็คือการใช้เว็บคอนโทรล Xml พิเศษ ที่แสดงผลด้วยตนเอง โดยการแปลง XSL ไปเป็นเอกสาร XML

คลาส  XmlDocument ช่วยรองรับการใช้งาน XML DOM ใน .NET และสามารถใช้คู่กับคอนโทรลที่รองรับการเชื่อมโยงข้อมูลโดยตรงได้ ส่วนคลาสหลักที่ใช้ค้นหา  DOM ใน XmlDocument ก็คือ XmlNode ซึ่งใช้เป็นตัวแทนขององค์ประกอบอย่างหนึ่งในเอกสาร โชคดีสำหรับพวกเราที่คลาส XmlNode ใช้ IEnumerable เพื่อคืนค่า enumerator ไปยังลูกๆของมัน ซึ่งนั่นหมายความว่าเราสามารถใช้  XmlNode  ใดๆเป็นแหล่งข้อมูลสำหรับการเชื่อมโยงข้อมูลได้ นอกจากนั้นปรากฏว่า  XmlDocumnet  ยังได้รับมาจาก XmlNode อีกด้วย ซึ่งในรูปของเอกสาร มันจึงเป็นแค่โหนดเดียวที่มีลูกพ่วงมาด้วย ดังนั้นการค้นหาจะทำได้ง่ายดายอย่างยิ่ง โดยเราจะยกตัวอย่างเอกสาร XML ที่จัดเก็บเอาไว้ในไฟล์ publishers.xml

<publishers>
  <publisher name="New Moon Books" city="Boston"
             state="MA" country="USA">
    <author name="Albert Ringer   ">
      <title name="Is Anger the Enemy?" />
      <title name="Life Without Fear" />
    </author>
    <author name="John White   ">
      <title name="Prolonged Data Deprivation " />
    </author>
    <author name="Charlene Locksley   ">
      <title name="Emotional Security: A New Algorithm" />
    </author>
    <author name="Marjorie Green   ">
      <title name="You Can Combat Computer Stress!" />
    </author>
  </publisher>
  <publisher name="Binnet and Hardley" city="Washington" 
             state="DC" country="USA">
    <author name="Sylvia Panteley   ">
      <title name="Onions, Leeks, and Garlic" />
    </author>
    <author name="Burt Gringlesby   ">
      <title name="Sushi, Anyone?" />
    </author>
    <author name="Innes del Castillo   ">
      <title name="Silicon Valley Gastronomic Treats" />
    </author>
    <author name="Michel DeFrance   ">
      <title name="The Gourmet Microwave" />
    </author>
    <author name="Livia Karsen   ">
      <title name="Computer Phobic AND Non-Phobic" />
    </author>
  </publisher>
  <!-- ... -->
</publishers>

เราสามารถโหลดไฟล์นี้ไปไว้ในคลาส  XmlDocument  ใน  Load  handler  ของเพจได้ จากนั้นทำการเชื่อมโยงองค์ประกอบระดับบนสุดของข้อมูล Publishers ไปยัง repeater ดังนี้

    private void Page_Load(object sender, EventArgs e)
   {
      XmlDocument doc = new XmlDocument();
      doc.Load(Server.MapPath("~/Publishers.xml"));
      _rep1.DataSource = doc.FirstChild;
      _rep1.DataBind();
    }

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

<asp:Repeater id="_rep1" runat="server"
              EnableViewState="false">
  <itemTemplate>
    Publisher: <%# ((XmlNode)Container.DataItem).
                    Attributes["name"].Value %><br/>
    <asp:Repeater runat="server" EnableViewState="false"
         DataSource='<%# Container.DataItem %>' >
      <itemTemplate>
        &nbsp;&nbsp;Author: <%# 
          ((XmlNode)Container.DataItem)
                            .Attributes["name"].Value 
                              %><br/>
        <asp:Repeater runat="server" EnableViewState="false"
                   DataSource='<%# Container.DataItem %>' >
           <itemTemplate>
              &nbsp;&nbsp;&nbsp;&nbsp;<i>
           <%# ((XmlNode)Container.DataItem).
                         Attributes["name"].Value %>
           </i><br />
           </itemTemplate>
         </asp:Repeater>
      </itemTemplate>
    </asp:Repeater>
    <hr />
  </itemTemplate>
 </asp:Repeater>

และแสดงผลออกมาดังนี้

 

ภาพที่ 5 ทดสอบการเชื่อมโยงข้อมูล

ขั้นตอนการเชื่อมโยงไปยังข้อมูล    XML   ค่อนข้างแตกต่างจากสองตัวอย่างก่อนหน้านี้ ก่อนอื่นเราต้องขอบอกว่า Datasource  expression ที่เรากำหนดเอาไว้ค่อนข้างเรียบง่ายอย่างมาก นั่นก็คือ Container.DataItem นั่นเอง สาเหตุเนื่องจากแหล่งข้อมูลของการเชื่อมโยงข้อมูลแต่ละระดับเป็น XmlNode อันหนึ่ง ซึ่งใช้เป็นตัวกำหนดลูกๆของมัน นอกจากนั้นถ้าหากลองสังเกต เราจะพบว่าการแยกข้อมูลออกจากรายการข้อมูลปัจจุบันใช้วิธีส่ง Container.DataItem  ไปยัง  XmlNode แล้วตัด (ในกรณีนี้) attribute ออกมา ส่วน DataBinder.Eval() method ที่ปกติจะใช้งานได้อย่างสะดวกจะไม่มีประโยชน์ในกรณีนี้เนื่องจากมันถูกออกแบบให้ทำงานกับแหล่งข้อมูลดาต้าเบส ไม่ใช่แหล่งข้อมูล XML

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

<animals>
  <animal>
    <name>Dog</name>
   <sound>woof</sound>
   <hasHair>true</hasHair>
  </animal>
  <animal>
    <name>Cat</name>
   <sound>meow</sound>
   <hasHair>true</hasHair>
  </animal>
  <animal>
    <name>Pig</name>
   <sound>oink</sound>
   <hasHair>false</hasHair>
  </animal>
</animals>

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

<asp:Repeater ID="_animalRep" Runat="server"
              EnableViewState="false">
   <ItemTemplate>
     <asp:Repeater Runat="server" EnableViewState="false"
                   DataSource='<%# Container.DataItem %>' >
       <ItemTemplate>
         <%# ((XmlNode)Container.DataItem).InnerText %><br
           />
       </ItemTemplate>
     </asp:Repeater>
     <hr />
   </ItemTemplate>
</asp:Repeater>

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

เมื่อถึงจุดนี้สิ่งที่เห็นได้อย่างชัดเจนก็คือคอนโทรลเชื่อมโยงข้อมูลใน   ASP.NET  ไม่ได้ถูกออกแบบมาให้เชื่อมโยงกับเอกสาร  XML ที่แยกสาขาอิสระ ดังนั้นวิธีการที่สะดวกมากกว่าก็คือการใช้ภาษาแปลงสภาพ XML ชื่อ XSL ที่มีอยู่เพื่อแสดงผล  XML  แทน โดยที่ ASP.NET ได้จัดเตรียมวิธีการง่ายๆสำหรับการทำงานนี้แล้ว แม้จะเป็นการแสดงผลแค่เพียงบางส่วนของเพจโดยใช้คอนโทรล  Xml  ก็ทำได้ ระบบจะรับอินพุดเป็นเอกสาร XML จากตัวแปลงสภาพ XSL จากนั้นทำการแสดงผลโดยการนำเอาตัวแปลงสภาพไปใช้กับเอกสาร ดังนั้นถ้าหากดูจากตัวอย่างเอกสาร  XML สัตว์ต่างๆของเรา เราอาจเขียนโค้ด animal.xsl ให้มีหน้าตาแบบนี้ได้

<xsl:transform 
   version="1.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="animal">
    <hr />
   <xsl:apply-templates />
  </xsl:template>

  <xsl:template match="name">
    <i><xsl:value-of select="." /></i><br/>
  </xsl:template>

  <xsl:template match="sound">
    <b><xsl:value-of select="." />!!!</b><br/>
  </xsl:template>

  <xsl:template match="hasHair">
    Has hair? <xsl:value-of select="." /><br/>
  </xsl:template>

</xsl:transform>
<asp:Xml Runat="server" 
         DocumentSource="animals.xml" 
         TransformSource="animals.xsl" />

และจะได้ผลลัพธ์ออกมาดังนี้

 

ภาพที่ 6: การแสดงผล animals.xsl

การเรียกใช้คอนโทรลเชิงซ้อน

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

เพื่อเป็นการสาธิตการใช้เทคนิคนี้ เราสามารถใช้ตัวอย่างแรกของการเชื่อมโยง Repeater ไปยังข้อมูลพิเศษ และสร้างภาพของกรอบกาเครื่องหมายให้แก่แต่ละ  item  และ item ย่อยได้ ถ้าหากผู้ใช้กาเครื่องหมายลงไปในกรอบกาเครื่องหมายช่องใดช่องหนึ่ง แล้วส่งเพจมา เราสามารถพิมพ์ข้อเท็จจริงดังกล่าวออกมาว่าการกาเครื่องหมายที่อยู่ด้านล่างของเพจให้เป็น Label ได้ ซึ่งไฟล์ .aspx จะมีหน้าตาแบบนี้

<asp:Repeater Runat="server" ID="_itemsRepeater" 
              EnableViewState="False">
  <ItemTemplate>
    <asp:CheckBox Runat="server" 
         Text='<%# DataBinder.Eval(Container.DataItem, 
                   "Name") %>'
            OnCheckedChanged="OnCheckedItem" />
    <asp:Repeater Runat="server" ID="_subitemsRepeater" 
                  EnableViewState="False"
         DataSource='<%# DataBinder.Eval(Container.DataItem, 
                                         "SubItems") %>'>
      <ItemTemplate>
        <br/>&nbsp;&nbsp;&nbsp;
        <asp:CheckBox Runat="server"
             Text='<%# DataBinder.Eval(Container.DataItem, 
                                       "Name") %>'
             OnCheckedChanged="OnCheckedItem" />
        <asp:Repeater Runat="server" EnableViewState="False"
          DataSource='<%# 
            DataBinder.Eval(Container.DataItem, 
                                          "SubItems") %>'>
          <ItemTemplate>
            <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <asp:CheckBox Runat="server"
               Text='<%# DataBinder.Eval(Container.DataItem, 
                                         "Name") %>' 
               OnCheckedChanged="OnCheckedItem"/>
          </ItemTemplate>
        </asp:Repeater>
      </ItemTemplate>
    </asp:Repeater>
    <br />
  </ItemTemplate>
</asp:Repeater>

<asp:Button Runat="server" Text="Submit" />

<asp:Label EnableViewState="False" Runat="server"
           ID="_message" />

ในตอนนี้เมื่อใดก็ตามที่ไคล์เอ็นต์โพสต์เพจมา ระบบจะมีการเรียก OnCheckedItem handler ทุกครั้งสำหรับแต่ละ item ที่สภาพมีการเปลี่ยนแปลงได้ (กาเครื่องหมายเปลี่ยนไปเป็นไม่กาเครื่องหมายหรือไม่กาเครื่องหมายเปลี่ยนเป็นกาเครื่องหมาย)      เราสามารถแยกแยะว่าคอนโทรลตัวใดมีการเปลี่ยนแปลงโดยไคล์เอ็นต์โดยดูที่     sender parameter   ที่ส่งไปให้ตัวจัดการเหตุการณ์ของเรา ตัวอย่างของการใช้ตัวจัดการเหตุการณ์ที่เขียนข้อความไปยังเพจระบุว่ากรอบกาเครื่องหมายมีการเปลี่ยนแปลงสภาพแสดงเอาไว้ด้านล่างนี้

protected void OnCheckedItem(object sender, EventArgs e)
{
  CheckBox cb = sender as CheckBox;
  if (cb.Checked)
      _message.Text += string.Format("{0} was checked<br 
        />", 
                                     cb.Text);
  else
     _message.Text += string.Format("{0} was 
       unchecked<br/>", 
                                    cb.Text);
}

การเชื่อมโยงแยกสาขาสำหรับ DataGrid และ DataList

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

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

 

ภาพที่ 7 DataSet ที่มีข้อมูลของ Northwind

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

 

ภาพที่ 8 DataSet ที่มีข้อมูลของ Northwind

 

ข้อจำกัดและประสิทธิภาพ

สิ่งสำคัญที่ต้องจำเอาไว้ก็คือกลไกการเชื่อมโยงข้อมูลใน   ASP.NET  ถูกออกแบบมาเพื่อเชื่อมโยงกับแหล่งข้อมูลแบบแบนราบเท่านั้น และแม้ว่ามันสามารถใช้เพื่อเชื่อมโยงข้อมูลแยกสาขาได้เช่นกันก็ตาม แต่มันก็ไม่ใช่วิธีการที่ดีที่สุดสำหรับการแสดงผลข้อมูลแยกสาขาจริงๆแต่อย่างใด แหล่งข้อมูลจำเป็นต้องมีรูปร่างปกติ และเราไม่อาจเชื่อมโยงไปหาแหล่งข้อมูลที่ลึกลงไปสองระดับได้ในบางจุดรวมทั้งข้อมูลระดับ  4 หรือ 5 ในจุดอื่นๆได้ ส่วน XSL เหมาะสำหรับการแสดงผลข้อมูลที่มีรูปทรงแยกสาขาที่ไม่แน่นอน ดังนั้นคุณควรแปลงข้อมูลไปเป็น  XML  และใช้  XSL แปลงสภาพร่วมกับคอนโทรล ASP Xml น่าจะเป็นทางเลือกที่ดีที่สุด

ถ้าสังเกตให้ดี คุณจะพบว่าตัวอย่างทั้งหมดในบทความนี้กำหนดให้  EnableViewState  flag  ในคอนโทรลเชื่อมโยงข้อมูลแต่ละอันเป็น  false  เราใช้  ViewState  เพื่อจัดเก็บสภาพแทนคอนโทรลระหว่าง POST requests ไปยังเพจเดิม โดยปกติแล้วคอนโทรลทั้งหมดในฝั่งเซิร์ฟเวอร์ของ  ASP.NET จะจัดเก็บสภาพของตนเอาไว้ระหว่าง POST  requests วิธีนี้ก่อให้เกิดความสะดวกเนื่องจากคุณสามารถใช้ประโยชน์จากสภาพที่เก็บเอาไว้ในคอนโทรลทั้งหมดของคุณได้

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

ส่วนใหญ่แล้วระบบจะไม่ได้ใช้เนื้อหาของ    ViewState   เนื่องจากคอนโทรลเชื่อมโยงข้อมูลจะถูกเชื่อมโยงไปยังแหล่งข้อมูลซ้ำเมื่อได้รับคำสั่ง ไม่ว่าจะเป็นคำสั่ง  GET แรกที่ไปยังเพจหรือคำสั่ง POST ต่อๆมาที่ย้อนกลับไปยังเพจเดิมก็ตาม ดังนั้นถ้าหากคุณไม่มีเหตุผลอื่นๆที่ดีพอแล้วละก็ ผมขอแนะนำให้คุณกำหนด  EnableViewState  เป็น False   ในคอนโทรลเชื่อมโยงข้อมูลทุกอันของคุณ โดยเฉพาะอย่างยิ่งถ้าหากคุณใช้เทคนิคเชื่อมโยงข้อมูลแยกสาขาตามที่ระบุเอาไว้ในบทความนี้

แต่คุณสามารถเปิดการทำงานของ  ViewState  สำหรับคอนโทรลเชิงซ้อนในฝั่งเซิร์ฟเวอร์ที่อยู่ใน ItemTemplate ของคอนโทรลเชื่อมโยงข้อมูลได้เหมือนอย่างที่เราทำกับคอนโทรล  CheckBox ที่อยู่ภายในตัวอย่าง Repeater ของเรา แต่คุณต้องจำเอาไว้ว่าต้องปิด ViewState ของคอนโทรลเชื่อมโยงข้อมูลจริงด้วย ถ้าหากคุณเปิด ViewState ในคอนโทรล  Repeater ทั้งหมดในตัวอย่างของเราที่เชื่อมโยงกับ DataSet อยู่ คุณจะพบว่าฟิลด์ ViewState จะโตขึ้นเป็น 250,000 ตัวอักษรเลยทีเดียว ในขณะที่ตัวอักษรที่ใช้ในการแสดงผลเพจมีแค่ 100,000 ตัวอักษรเท่านั้น

 

สรุป

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

Filed under: ,
 

มาแอบอ่าน said:

ขอบคุณครับ เป็นบทความที่มีประโยชน์มากๆ ครับ เป็นกำลังใจให้เขียนบทความดีๆ แบบนี้ออกมาเรื่อยๆ นะครับ

March 9, 2009 10:07 AM

Leave a Comment

(required)  
(optional)
(required)  
Add