Associazione di dati a controlli (WCF Data Services)
Con WCF Data Services è possibile associare controlli quali ComboBox e ListView a un'istanza della classe DataServiceCollection<T>. Questa raccolta, che eredita dalla classe ObservableCollection<T>, contiene i dati da un feed OData (Open Data Protocol). Questa classe rappresenta una raccolta di dati dinamica che fornisce notifiche in caso di aggiunta o rimozione di elementi. Quando si utilizza un'istanza di DataServiceCollection<T> per l'associazione dati, le librerie client di WCF Data Services gestiscono questi eventi per garantire che gli oggetti rilevati da DataServiceContext rimangano sincronizzati con i dati nell'elemento dell'interfaccia utente associato.
La classe DataServiceCollection<T> implementa indirettamente l'interfaccia INotifyCollectionChanged per avvisare il contesto dell'aggiunta o della rimozione di oggetti dalla raccolta. Gli oggetti del tipo di servizio dati utilizzati con DataServiceCollection<T> devono inoltre implementare l'interfaccia INotifyPropertyChanged per informare l'oggetto DataServiceCollection<T> quando le proprietà di oggetti inclusi nella raccolta di associazioni vengono modificate.
Nota
Quando si utilizza la finestra di dialogo Aggiungi riferimento al servizio o lo strumentoDataSvcUtil.exe con l'opzione /dataservicecollection per generare classi del servizio dati client, le classi di dati generate implementano l'interfaccia INotifyPropertyChanged.Per ulteriori informazioni, vedere Procedura: generare in modo manuale classi del servizio dati client (WCF Data Services).
Creazione della raccolta di associazioni
Creare una nuova istanza della classe DataServiceCollection<T> chiamando uno dei metodi del costruttore di classi con un'istanza di DataServiceContext fornita e facoltativamente un oggetto DataServiceQuery<TElement> o una query LINQ che al termine dell'esecuzione restituisce un'istanza di IEnumerable<T>. Questo oggetto IEnumerable<T> fornisce l'origine degli oggetti della raccolta di associazioni, che vengono materializzate da un feed OData. Per ulteriori informazioni, vedere Materializzazione di oggetti (WCF Data Services). Per impostazione predefinita, le modifiche apportate agli oggetti associati e agli elementi inseriti nella raccolta vengono rilevate automaticamente dall'oggetto DataServiceContext. Se è necessario rilevare manualmente queste modifiche, chiamare uno dei metodi del costruttore che accetta un parametro trackingMode e specificare il valore None.
Nell'esempio seguente viene illustrato come creare un'istanza di DataServiceCollection<T> in base a un oggetto DataServiceContext fornito e un oggetto DataServiceQuery<TElement> che restituisce tutti i clienti con gli ordini correlati:
' Create a new collection that contains all customers and related orders.
Dim trackedCustomers As DataServiceCollection(Of Customer) = _
New DataServiceCollection(Of Customer)(context.Customers.Expand("Orders"))
// Create a new collection that contains all customers and related orders.
DataServiceCollection<Customer> trackedCustomers =
new DataServiceCollection<Customer>(context.Customers.Expand("Orders"));
Associazione di dati a elementi di Windows Presentation Foundation
Poiché la classe DataServiceCollection<T> eredita dalla classe ObservableCollection<T>, è possibile associare oggetti a un elemento o a un controllo in un'applicazione di Windows Presentation Foundation (WPF) in modo analogo a quando si utilizza la classe ObservableCollection<T> per l'associazione. Per ulteriori informazioni, vedere Associazione dati (Windows Presentation Foundation). Un modo per associare dati del servizio dati a controlli WPF consiste nell'impostare la proprietà DataContext dell'elemento sull'istanza della classe DataServiceCollection<T> che contiene il risultato della query. In questo caso, è possibile utilizzare la proprietà ItemsSource per impostare l'origine dell'oggetto per il controllo. Utilizzare la proprietà DisplayMemberPath per specificare quale proprietà dell'oggetto associato visualizzare. Quando si associa un elemento a un oggetto correlato restituito da una proprietà di navigazione, includere il percorso nell'associazione definita per la proprietà ItemsSource. Questo percorso è relativo all'oggetto radice impostato dalla proprietà DataContext del controllo padre. Nell'esempio seguente viene impostata la proprietà DataContext di un elemento StackPanel per associare il controllo padre a un oggetto DataServiceCollection<T> di oggetti personalizzati:
' Create a LINQ query that returns customers with related orders.
Dim customerQuery = From cust In context.Customers.Expand("Orders") _
Where cust.Country = customerCountry _
Select cust
' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery)
' Bind the root StackPanel element to the collection
' related object binding paths are defined in the XAML.
Me.LayoutRoot.DataContext = trackedCustomers
' Create a LINQ query that returns customers with related orders.
Dim customerQuery = From cust In context.Customers.Expand("Orders") _
Where cust.Country = customerCountry _
Select cust
' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customers)(customerQuery, _
TrackingMode.AutoChangeTracking, "Customers", _
AddressOf OnMyPropertyChanged, AddressOf OnMyCollectionChanged)
' Bind the root StackPanel element to the collection
' related object binding paths are defined in the XAML.
Me.LayoutRoot.DataContext = trackedCustomers
Me.LayoutRoot.UpdateLayout()
' Create a LINQ query that returns customers with related orders.
Dim customerQuery = From cust In context.Customers.Expand("Orders") _
Where cust.Country = customerCountry _
Select cust
' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery, _
TrackingMode.AutoChangeTracking, "Customers", _
AddressOf OnMyPropertyChanged, AddressOf OnMyCollectionChanged)
' Bind the root StackPanel element to the collection
' related object binding paths are defined in the XAML.
Me.LayoutRoot.DataContext = trackedCustomers
// Create a LINQ query that returns customers with related orders.
var customerQuery = from cust in context.Customers.Expand("Orders")
where cust.Country == customerCountry
select cust;
// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery,
TrackingMode.AutoChangeTracking,"Customers",
OnPropertyChanged, OnCollectionChanged);
// Bind the root StackPanel element to the collection;
// related object binding paths are defined in the XAML.
this.LayoutRoot.DataContext = trackedCustomers;
// Create a LINQ query that returns customers with related orders.
var customerQuery = from cust in context.Customers.Expand("Orders")
where cust.Country == customerCountry
select cust;
// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery);
// Bind the root StackPanel element to the collection;
// related object binding paths are defined in the XAML.
LayoutRoot.DataContext = trackedCustomers;
Nell'esempio seguente viene illustrata la definizione dell'associazione XAML dei controlli figlio DataGrid e ComboBox:
<StackPanel Orientation="Vertical" Height="Auto" Name="LayoutRoot" Width="Auto">
<Label Content="Customer ID" Margin="20,0,0,0" />
<ComboBox Name="customerIDComboBox" DisplayMemberPath="CustomerID" ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True" SelectedIndex="0" Height="23" Width="120"
HorizontalAlignment="Left" Margin="20,0,0,0" VerticalAlignment="Center" />
<ListView ItemsSource="{Binding Path=Orders}" Name="ordersDataGrid" Margin="34,46,34,50">
<ListView.View>
<GridView AllowsColumnReorder="False" ColumnHeaderToolTip="Line Items">
<GridViewColumn DisplayMemberBinding="{Binding Path=OrderID, Mode=OneWay}"
Header="Order ID" Width="50"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=OrderDate, Mode=TwoWay}"
Header="Order Date" Width="50"/>
<GridViewColumn DisplayMemberBinding="{Binding Path=Freight, Mode=TwoWay}"
Header="Freight Cost" Width="50"/>
</GridView>
</ListView.View>
</ListView>
<Button Name="saveChangesButton" Content="Save Changes" Click="saveChangesButton_Click"
Width="80" Height="30" Margin="450,0,0,0"/>
</StackPanel>
Per ulteriori informazioni, vedere Procedura: associare dati a elementi Windows Presentation Foundation (WCF Data Services).
Quando un'entità partecipa a una relazione uno-a-molti o molti-a-molti, la proprietà di navigazione per la relazione restituisce una raccolta di oggetti correlati. Quando si utilizza la finestra di dialogo Aggiungi riferimento al servizio o lo strumento DataSvcUtil.exe per generare classi del servizio dati client, la proprietà di navigazione restituisce un'istanza di DataServiceCollection<T>. In questo modo è possibile associare oggetti correlati a un controllo e supportare scenari di associazione WPF comuni, quale il modello di associazione Master-Details per entità correlate. Nell'esempio XAML precedente il codice XAML determina l'associazione dell'oggetto DataServiceCollection<T> master all'elemento dati radice. L'oggetto DataGrid dell'ordine viene quindi associato all'oggetto DataServiceCollection<T> relativo agli ordini restituito dall'oggetto Customers selezionato, che viene a sua volta associato all'elemento dati radice di Window.
Associazione di dati a controlli Windows Form
Per associare dati a un controllo Windows Form, impostare la proprietà DataSource del controllo sull'istanza della classe DataServiceCollection<T> che contiene il risultato della query.
Nota
L'associazione dati è supportata solo per i controlli che sono in attesa di eventi di modifica mediante l'implementazione delle interfacce INotifyCollectionChanged e INotifyPropertyChanged.Quando un controllo non supporta questo tipo di notifica delle modifiche, le modifiche apportate all'oggetto DataServiceCollection<T> sottostante non sono riflesse nel controllo associato.
Nell'esempio seguente viene associato un oggetto DataServiceCollection<T> a un controllo ComboBox:
' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery)
'Bind the Customers combobox to the collection.
customersComboBox.DisplayMember = "CustomerID"
customersComboBox.DataSource = trackedCustomers
// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery);
//Bind the Customers combobox to the collection.
customersComboBox.DisplayMember = "CustomerID";
customersComboBox.DataSource = trackedCustomers;
Quando si utilizza la finestra di dialogo Aggiungi riferimento al servizio per generare le classi del servizio dati client, viene inoltre creata un'origine dati del progetto in base all'oggetto DataServiceContext generato. Questa origine dati consente di creare elementi o controlli dell'interfaccia utente per la visualizzazione dei dati del servizio dati semplicemente trascinando elementi dalla finestra Origini dati nella finestra di progettazione. Nell'interfaccia utente dell'applicazione questi elementi diventano elementi associati all'origine dati. Per ulteriori informazioni, vedere Procedura: associare dati utilizzando un'origine dati del progetto (WCF Data Services).
Associazione di dati di paging
È possibile configurare un servizio dati per limitare la quantità di dati sottoposti a query che vengono restituiti in un unico messaggio di risposta. Per ulteriori informazioni, vedere Configurazione del servizio dati (WCF Data Services). Quando il servizio dati esegue il paging dei dati di risposta, in ogni risposta è incluso un collegamento che consente di restituire la pagina successiva di risultati. Per ulteriori informazioni, vedere Caricamento di contenuto posticipato (WCF Data Services). In questo caso, è necessario caricare in modo esplicito le pagine chiamando il metodo Load su DataServiceCollection<T> e passando l'URI ottenuto dalla proprietà NextLinkUri, come illustrato nell'esempio seguente:
' Create a new collection for binding based on the LINQ query.
trackedCustomers = New DataServiceCollection(Of Customer)(customerQuery)
' Load all pages of the response at once.
While trackedCustomers.Continuation IsNot Nothing
trackedCustomers.Load( _
context.Execute(Of Customer)(trackedCustomers.Continuation.NextLinkUri))
End While
// Create a new collection for binding based on the LINQ query.
trackedCustomers = new DataServiceCollection<Customer>(customerQuery);
// Load all pages of the response at once.
while (trackedCustomers.Continuation != null)
{
trackedCustomers.Load(
context.Execute<Customer>(trackedCustomers.Continuation.NextLinkUri));
}
Gli oggetti correlati vengono caricati in modo analogo. Per ulteriori informazioni, vedere Procedura: associare dati a elementi Windows Presentation Foundation (WCF Data Services).
Personalizzazione dei comportamenti di associazione dati
La classe DataServiceCollection<T> consente di intercettare gli eventi generati nel momento in cui vengono apportate modifiche alla raccolta, ad esempio l'aggiunta o la rimozione di un oggetto e la modifica delle proprietà di un oggetto in una raccolta. È possibile modificare gli eventi di associazione dati per eseguire l'override del comportamento predefinito che include i vincoli seguenti:
All'interno dei delegati non viene eseguita alcuna convalida.
L'aggiunta di un'entità determina automaticamente l'aggiunta delle entità correlate.
L'eliminazione di un'entità non determina l'eliminazione delle entità correlate.
Quando si crea una nuova istanza di DataServiceCollection<T>, è possibile specificare i parametri seguenti che definiscono i delegati per i metodi che gestiscono gli eventi generati al momento della modifica degli oggetti associati:
entityChanged: metodo che viene chiamato quando la proprietà di un oggetto associato viene modificata. Questo delegato Func<T, TResult> accetta un oggetto EntityChangedParams e restituisce un valore booleano che indica se il comportamento predefinito, che implica la chiamata del metodo UpdateObject su DataServiceContext, deve ancora verificarsi.
entityCollectionChanged: metodo che viene chiamato quando un oggetto viene aggiunto o rimosso dalla raccolta di associazioni. Questo delegato Func<T, TResult> accetta un oggetto EntityCollectionChangedParams e restituisce un valore booleano che indica se il comportamento predefinito, che implica la chiamata del metodo AddObject per un'azione Add o del metodo DeleteObject per un'azione Remove sull'oggetto DataServiceContext, deve ancora verificarsi.
Nota
WCF Data Services non esegue alcuna convalida dei comportamenti personalizzati implementati in questi delegati.
Nell'esempio seguente l'azione Remove viene personalizzata per chiamare il metodo DeleteLink e il metodo DeleteObject per rimuovere le entità Orders_Details che appartengono a un'entità Orders eliminata. Questa azione personalizzata viene eseguita in quanto le entità dipendenti non vengono eliminate automaticamente al momento dell'eliminazione dell'entità padre.
' Method that is called when the CollectionChanged event is handled.
Private Function OnMyCollectionChanged( _
ByVal entityCollectionChangedinfo As EntityCollectionChangedParams) As Boolean
If entityCollectionChangedinfo.Action = _
NotifyCollectionChangedAction.Remove Then
' Delete the related items when an order is deleted.
If entityCollectionChangedinfo.TargetEntity.GetType() Is GetType(Orders) Then
' Get the context and object from the supplied parameter.
Dim context = entityCollectionChangedinfo.Context
Dim deletedOrder As Orders = _
CType(entityCollectionChangedinfo.TargetEntity, Orders)
' Load the related OrderDetails.
context.LoadProperty(deletedOrder, "Order_Details")
' Delete the order and its related items
For Each item As Order_Details In deletedOrder.Order_Details
'context.DeleteLink(deletedOrder, "Order_Details", item)
context.DeleteObject(item)
Next
' Delete the order and then return false since the object is already deleted.
context.DeleteObject(deletedOrder)
Return False
Else
Return True
End If
Else
' Use the default behavior.
Return True
End If
End Function
' Method that is called when the CollectionChanged event is handled.
Private Function OnMyCollectionChanged( _
ByVal entityCollectionChangedinfo As EntityCollectionChangedParams) As Boolean
If entityCollectionChangedinfo.Action = _
NotifyCollectionChangedAction.Remove Then
' Delete the related items when an order is deleted.
If entityCollectionChangedinfo.TargetEntity.GetType() Is GetType(Order) Then
' Get the context and object from the supplied parameter.
Dim context = entityCollectionChangedinfo.Context
Dim deletedOrder As Order = _
CType(entityCollectionChangedinfo.TargetEntity, Order)
If deletedOrder.Order_Details.Count = 0 Then
' Load the related OrderDetails.
context.LoadProperty(deletedOrder, "Order_Details")
End If
' Delete the order and its related items
For Each item As Order_Detail In deletedOrder.Order_Details
context.DeleteObject(item)
Next
' Delete the order and then return false since the object is already deleted.
context.DeleteObject(deletedOrder)
Return True
Else
Return False
End If
Else
' Use the default behavior.
Return False
End If
End Function
// Method that is called when the CollectionChanged event is handled.
private bool OnCollectionChanged(
EntityCollectionChangedParams entityCollectionChangedinfo)
{
if (entityCollectionChangedinfo.Action ==
NotifyCollectionChangedAction.Remove)
{
// Delete the related items when an order is deleted.
if (entityCollectionChangedinfo.TargetEntity.GetType() == typeof(Order))
{
// Get the context and object from the supplied parameter.
DataServiceContext context = entityCollectionChangedinfo.Context;
Order deletedOrder = entityCollectionChangedinfo.TargetEntity as Order;
if (deletedOrder.Order_Details.Count == 0)
{
// Load the related OrderDetails.
context.LoadProperty(deletedOrder, "Order_Details");
}
// Delete the order and its related items;
foreach (Order_Detail item in deletedOrder.Order_Details)
{
context.DeleteObject(item);
}
// Delete the order and then return true since the object is already deleted.
context.DeleteObject(deletedOrder);
return true;
}
else
{
return false;
}
}
else
{
// Use the default behavior.
return false;
}
}
Per ulteriori informazioni, vedere Procedura: personalizzare i comportamenti di associazione dati (WCF Data Services).
Il comportamento predefinito che si verifica quando un oggetto viene rimosso da un oggetto DataServiceCollection<T> tramite il metodo Remove, consiste nel fatto che l'oggetto viene contrassegnato come eliminato anche in DataServiceContext. Per modificare questo comportamento, è possibile specificare un delegato per un metodo nel parametro entityCollectionChanged che viene chiamato quando si verifica l'evento CollectionChanged.
Associazione dati con classi di dati client personalizzate
Per essere in grado di caricare oggetti in DataServiceCollection<T>, gli oggetti stessi devono implementare l'interfaccia INotifyPropertyChanged. Le classi client del servizio dati che vengono generate quando si utilizza la finestra di dialogo Aggiungi riferimento al servizio o lo strumento DataSvcUtil.exe implementano questa interfaccia. Se si forniscono classi di dati client personalizzate, sarà necessario utilizzare un altro tipo di raccolta per l'associazione dati. Quando gli oggetti vengono modificati, è necessario gestire gli eventi nei controlli associati ai dati per chiamare i metodi della classe DataServiceContext seguenti:
AddObject: quando un nuovo oggetto viene aggiunto alla raccolta.
DeleteObject: quando un oggetto viene rimosso dalla raccolta.
UpdateObject: quando una proprietà viene modificata in un oggetto della raccolta.
AddLink: quando un oggetto viene aggiunto alla raccolta di un oggetto correlato.
SetLink: quando un oggetto viene aggiunto a una raccolta di oggetti correlati.
Per ulteriori informazioni, vedere Aggiornamento del servizio dati (WCF Data Services).
Vedere anche
Attivitá
Procedura: generare in modo manuale classi del servizio dati client (WCF Data Services)
Procedura: aggiungere un riferimento al servizio dati (WCF Data Services)