Sulla Programmazione

Quattro chiacchere sulla programmazione e sulle bit-tecnologie con Fabrizio Cipriani

Data binding di query gerarchiche con custom objects in ASP.NET

Vediamo come possiamo gestire la necessità di visualizzare dati gerarchici durante il data binding di un controllo con il comando ADO SHAPE.

Il modo migliore per iniziare è con un esempio. Prendiamo il database di esempio Northwind della Microsoft, e proviamo ad ottenere i dati relativi alla tabella ORDERS ordinati gerarchicamente sotto i clienti della tabella CUSTOMERS:

1
2
3
4
5
SHAPE {select customerId, contactName   
  from CUSTOMERS}  
APPEND ({select orderid, employeeid  
  from ORDERS} as subOrders  
  RELATE CustomerID to CustomerID )

Nell'esempio vengono selezionate le colonne customerId, contactName e la colonna calcolata subOrders dalla tabella CUSTOMERS. Quest'ultima colonna, generata con il comando APPEND, contiene un sotto recordset con le colonne orderId e employeeid là dove il suo campo customerId è uguale al campo customerid della tabella CUSTOMERS (vedi l'uso di RELATE).

Definiamo prima la stringa di connessione nel web.config. Occorre ricordarsi di utilizzare il provider MSDataShape all'interno di SQLOLEDB, perchè fino a SQL SERVER 2005 è l'unico a fornire il comando SHAPE:

1
2
3
4
5
6
7
8
<connectionStrings>  
  <add name="OleDbConnectionString"   
    connectionString="Provider=MSDataShape;   
    Data Provider=SQLOLEDB;  
    Server=MyServer; Database=Northwind;   
    Integrated Security=SSPI"   
    providerName="System.Data.OleDb" />  
</connectionStrings>

Dobbiamo ora mappare i campi ritornati dalla query all'interno dei nostri Custom Objects. Definiamo quindi le classi da mappare li proprietà saranno mappati sui campi della query:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Customer  
{  
  public Customer(String customerid, String contactname)  
  {  
    CustomerId = customerid;  
    ContactName = contactname;  
    SubOrders = new List<Order>();  
  }

  private String _customerId;  
  public String CustomerId  
  {  
    get { return _customerId; }  
    set { _customerId = value; }    
  }

  private String _contactName;  
  public String ContactName  
  {  
    get { return _contactName; }  
    set { _contactName = value; }  
  }

  private List<Order> _subOrders;  
  public List<Order> SubOrders  
  {  
    get { return _subOrders; }  
    set { _subOrders = value; }  
  }  
}

public class Order  
{  
  public Order(int orderid,   
   int employeeide)  
  {  
    OrderId = orderid;  
    EmployeeId = employeeid;  
  }

  private int _orderId;  
  public int OrderId  
  {  
    get { return _orderId; }  
    set { _orderId = value; }  
  }

  private int _employeeId;  
  public int EmployeeId  
  {  
    get { return _employeeId; }  
    set { _employeeId = value; }  
  }  
}

Come vedete, la classe Customer ha una proprietà SubOrders che contiene una lista di ordini. Quando visualizzeremo gli items di un controllo Repeater nella pagina aspx, potremo usare questa proprietà come DataSource per un sotto-repeater e visualizzare le informazioni della sottolista in modo gerarchico.

Creiamo ora il metodo che interroga il database e popola le liste:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public List<Customer> GetCustomers()  
{  
  List<Customer> customers =   
    new List<Customer>();

  using (OleDbConnection cn = new OleDbConnection(  
      ConfigurationManager.ConnectionStrings["OLEConnectionString"].ToString())
        )  
  {  
    OleDbCommand cmd = new OleDbCommand(  
      " SHAPE {select CustomerId, ContactName " +   
      "  FROM CUSTOMERS} " +  
      " APPEND ({select CustomerId, OrderId, " +  
      "   EmployeeId FROM ORDERS} " +   
      "   as SubOrders " +  
      " RELATE CustomerId to CustomerId)", cn);

    cn.Open();  
    OleDbDataReader rdr = cmd.ExecuteReader();

    while (rdr.Read())  
    {  
      Customer customer =   
        new Customer(rdr["CustomerId"].ToString(),   
          rdr["ContactName"].ToString());

      if (rdr["SubOrders"] is IDataReader)  
      {   
        OleDbDataReader subrdr =   
          (OleDbDataReader)rdr["SubOrders"];

        while (subrdr.Read())  
        {  
          customer.SubOrders.Add(  
            new Order((int)subrdr["OrderId"],  
            (int)subrdr["EmployeeId"]));  
        }  
      }

      customers.Add(customer);  
    }  
  }

  return customers;  
}

Viene usato un reader per la lista di customers. Nel campo SubOrders del reader abbiamo invece una lista di DataReader che riportano i campi CustomerId, OrderId, EmployeeId con relazione Orders.CustomerId = Customer.CustomerId, esattamente come abbiamo impostato nella query SHAPE. Usiamo quindi quegli IDataReader per popolare la sottolista SubOrders dell'oggetto customer corrente.

Il metodo ritorna la lista customers inizializzata con oggetti Customer che al loro interno hanno le sottoliste SubOrders.

Il databinding ad un oggetto Repeater è semplice:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<asp:Repeater ID="Repeater1" runat="server">

 <ItemTemplate>  
  <asp:CheckBox ID="CheckBox1" runat="server"   
   Text='<%#   
     DataBinder.Eval(Container.DataItem,   
        "CustomerId") + ": " +   
     DataBinder.Eval(Container.DataItem,   
       "ContactName") %>' >    
   
  <asp:Repeater   
    Runat="server"   
    ID="_subitemsRepeater"  
    EnableViewState="false"  
    DataSource='<%#   
       DataBinder.Eval(Container.DataItem,   
       "SubOrders") %>'>  
    
  <ItemTemplate>  
     <br/>&nbsp;&nbsp;&nbsp;  
     <asp:CheckBox   
         ID="CheckBox1" Runat="server"  
         Text='<%#   
            DataBinder.Eval(Container.DataItem,   
               "OrderId") + ": " +   
            DataBinder.Eval(Container.DataItem,   
               "EmployeeId") %>'   
     \>  
  </ItemTemplate>  
</asp:Repeater>  
   
  <br\>  
 </ItemTemplate>

</asp:Repeater>

Il repeater di primo livello all'interno del proprio ha un sotto-repeater il cui datasource punta all'elemento SubOrders dell'oggetto Customer corrente. Da cui il sotto-item che visualizza i campi OrderId e EmployeeId.

Per finire, nel metodo Load_Page si effettua il DataBind vero e proprio:

1
2
3
4
5
6
protected void Page_Load(object sender,   
   EventArgs e)  
{  
 Repeater1.DataSource = GetCustomers();  
 Repeater1.DataBind();  
}

Ed ecco il risultato:

Comments