Utilizzo delle annotazioni con un DataSet tipizzato

Le annotazioni consentono di modificare i nomi degli elementi nel DataSet tipizzato, senza modificare lo schema sottostante. Se si modificassero i nomi degli elementi dello schema sottostante, i riferimenti presenti nel DataSet punterebbero a oggetti non più esistenti nell'origine dati e si perderebbe un riferimento agli oggetti esistenti nell'origine dati.

L'utilizzo delle annotazioni consente di personalizzare i nomi degli oggetti presenti nel DataSet tipizzato assegnando nomi più significativi, in modo da migliorare la leggibilità del codice e facilitare l'utilizzo del DataSet da parte dei client, pur mantenendo intatto lo schema sottostante. Dal seguente elemento di schema per la tabella Customers del database Northwind ad esempio si ottiene come risultato un nome oggetto DataRow corrispondente a CustomersRow e un oggetto DataRowCollection denominato Customers.

<xs:element name="Customers">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="CustomerID" type="xs:string" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

Il nome Customers per un oggetto DataRowCollection risulta significativo nel codice del client, mentre il nome CustomersRow per DataRow potrebbe generare confusione, poiché si tratta di un unico oggetto. Negli scenari più comuni inoltre ci si riferisce all'oggetto senza specificare l'identificatore Row e indicando tale oggetto nel riferimento semplicemente come oggetto Customer. La soluzione consiste nell'inserire annotazioni nello schema e nell'identificare nuovi nomi per gli oggetti DataRow e DataRowCollection. Di seguito viene riportata una versione annotata dello schema precedente.

<xs:element name="Customers" codegen:typedName="Customer" codegen:typedPlural="Customers">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="CustomerID" type="xs:string" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

Se si specifica un valore typedName pari a Customer, si otterrà un nome per l'oggetto DataRow corrispondente a Customer. Specificando un valore typedPlural pari a Customers, si conserva il nome Customers per DataRowCollection.

Nella seguente tabella sono indicate le annotazioni disponibili.

Annotazione Descrizione
typedName Nome dell'oggetto.
typedPlural Nome dell'insieme di oggetti.
typedParent Nome dell'oggetto quando si fa riferimento a tale oggetto in una relazione padre.
typedChildren Nome del metodo per la restituzione di oggetti da una relazione figlio.
nullValue Valore specificato se il valore sottostante è DBNull. Per le annotazioni di tipo nullValue, vedere la tabella seguente. Il valore predefinito è _throw.

Nella tabella seguente vengono elencati i valori che è possibile utilizzare per le annotazioni di tipo nullValue.

nullValue Descrizione
Valore di sostituzione Consente di specificare il valore da restituire. È necessario che il valore restituito corrisponda al tipo dell'elemento. Utilizzare ad esempio nullValue="0" per restituire 0 per i campi integer null.
_throw Generazione di un'eccezione. Si tratta dell'impostazione predefinita.
_null Consente di restituire un riferimento null o di generare un'eccezione se viene rilevato un tipo primitivo.
_empty Nel caso delle stringhe consente di restituire String.Empty, negli altri casi consente di restituire un oggetto creato da un costruttore vuoto. Se viene rilevato un tipo primitivo, consente di generare un'eccezione.

Nella tabella seguente vengono elencati i valori predefiniti per gli oggetti di un DataSet tipizzato e le annotazioni disponibili.

Oggetto/Metodo/Evento Valore predefinito Annotazione
DataTable TableNameDataTable typedPlural
Metodi DataTable NewTableNameRow

AddTableNameRow

DeleteTableNameRow

typedName
DataRowCollection TableName typedPlural
DataRow TableNameRow typedName
DataColumn DataTable.ColumnNameColumn

DataRow.ColumnName

typedName
Proprietà PropertyName typedName
Funzione di accesso figlio GetChildTableNameRows typedChildren
Funzione di accesso padre TableNameRow typedParent
Eventi DataSet TableNameRowChangeEvent

TableNameRowChangeEventHandler

typedName

Per utilizzare le annotazioni del DataSet tipizzato, è necessario includere il seguente riferimento xmlns nello schema XSD (Schema Definition Language) di XML.

xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"

Di seguito viene riportato un esempio di schema annotato in cui viene esposta la tabella Customers del database Northwind con inclusa una relazione alla tabella Orders.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="CustomerDataSet" 
      xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"
       
      xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="CustomerDataSet" msdata:IsDataSet="true">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element name="Customers" codegen:typedName="Customer" codegen:typedPlural="Customers">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="CustomerID" codegen:typedName="CustomerID" type="xs:string" minOccurs="0" />
              <xs:element name="CompanyName" codegen:typedName="CompanyName" type="xs:string" minOccurs="0" />
              <xs:element name="Phone" codegen:typedName="Phone" codegen:nullValue="" type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="Orders" codegen:typedName="Order" codegen:typedPlural="Orders">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="OrderID" codegen:typedName="OrderID" type="xs:int" minOccurs="0" />
              <xs:element name="CustomerID" codegen:typedName="CustomerID" codegen:nullValue="" type="xs:string" minOccurs="0" />
              <xs:element name="EmployeeID" codegen:typedName="EmployeeID" codegen:nullValue="0" type="xs:int" minOccurs="0" />
              <xs:element name="OrderDate" codegen:typedName="OrderDate" codegen:nullValue="1980-01-01T00:00:00" type="xs:dateTime" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
    <xs:unique name="Constraint1">
      <xs:selector xpath=".//Customers" />
      <xs:field xpath="CustomerID" />
    </xs:unique>
    <xs:keyref name="CustOrders" refer="Constraint1" codegen:typedParent="Customer" codegen:typedChildren="GetOrders">
      <xs:selector xpath=".//Orders" />
      <xs:field xpath="CustomerID" />
    </xs:keyref>
  </xs:element>
</xs:schema>

Nell'esempio di codice seguente viene utilizzato un DataSet tipizzato in modo sicuro creato dallo schema di esempio. Un DataAdapter viene utilizzato per compilare la tabella Customers e un altro DataAdapter per compilare la tabella Orders. Il DataSet tipizzato in modo sicuro consente di definire l'oggetto DataRelations.

Dim nwindConn As SqlConnection = New SqlConnection("Data Source=localhost;Integrated Security=SSPI;" & _
                                                   "Initial Catalog=northwind")
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT CustomerID, CompanyName, Phone FROM Customers", &
                                                  nwindConn)
Dim orderDA As SqlDataAdapter = New SqlDataAdapter("SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM Orders", &
                                                   nwindConn)

' Populate a strongly typed DataSet.
nwindConn.Open()
Dim custDS As CustomerDataSet = New CustomerDataSet()
custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")
nwindConn.Close()

' Add a strongly typed event.
AddHandler custDS.Customers.CustomerChanged, &
           New CustomerDataSet.CustomerChangeEventHandler(AddressOf OnCustomerChanged)

' Add a strongly typed DataRow.
Dim newCust As CustomerDataSet.Customer = custDS.Customers.NewCustomer()
newCust.CustomerID = "NEW01"
newCust.CompanyName = "My New Company"
custDS.Customers.AddCustomer(newCust)

' Navigate the child relation.
Dim customer As CustomerDataSet.Customer
Dim order As CustomerDataSet.Order

For Each customer In custDS.Customers
  Console.WriteLine(customer.CustomerID)
  For Each order In customer.GetOrders()
    Console.WriteLine(vbTab & order.OrderID)
  Next
Next

Private Shared Sub OnCustomerChanged(sender As Object, e As CustomerDataSet.CustomerChangeEvent)

End Sub
[C#]
SqlConnection nwindConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");
SqlDataAdapter custDA = new SqlDataAdapter("SELECT CustomerID, CompanyName, Phone FROM Customers", nwindConn);
SqlDataAdapter orderDA = new SqlDataAdapter("SELECT OrderID, CustomerID, EmployeeID, OrderDate FROM Orders", nwindConn);

// Populate a strongly typed DataSet.
nwindConn.Open();
CustomerDataSet custDS = new CustomerDataSet();
custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");
nwindConn.Close();

// Add a strongly typed event.
custDS.Customers.CustomerChanged += new 
  CustomerDataSet.CustomerChangeEventHandler(OnCustomerChanged);

// Add a strongly typed DataRow.
CustomerDataSet.Customer newCust = custDS.Customers.NewCustomer();
newCust.CustomerID = "NEW01";
newCust.CompanyName = "My New Company";
custDS.Customers.AddCustomer(newCust);

// Navigate the child relation.
foreach(CustomerDataSet.Customer customer in custDS.Customers)
{
  Console.WriteLine(customer.CustomerID);
  foreach(CustomerDataSet.Order order in customer.GetOrders())
    Console.WriteLine("\t" + order.OrderID);
}

protected static void OnCustomerChanged(object sender, CustomerDataSet.CustomerChangeEvent e)
{

}

Vedere anche

Utilizzo di un DataSet tipizzato | Creazione e utilizzo di DataSet | Classe DataColumnCollection | Classe DataSet