如何:投影数据服务查询结果(Silverlight 客户端)

投影是在开放式数据协议 (OData) 中定义的,它通过指定仅在响应中返回实体的某些属性来减少查询返回的源中的数据量。这样,可以将数据从数据服务的实体类型投影到客户端的不同实体类型。 通过使用 select 子句(在 Visual Basic 中为 Select)可以对 LINQ 查询的结果执行投影。此外,还可以调用 AddQueryOption 方法将 $select 子句添加到查询 URI。 有关更多信息,请参见查询数据服务(WCF 数据服务)

应用程序访问的 Northwind 示例数据服务是在完成 WCF 数据服务 快速入门时创建的。 此外,还可以使用 OData 网站上发布的公共 Northwind 示例数据服务;该示例数据服务是只读的,当尝试保存更改时会返回错误。

示例

以下示例演示一个 LINQ 查询,它将 Customer 实体投影到已在客户端应用程序中定义的 CustomerAddress 类型。 CustomerAddress 类是在客户端上定义的并且具有客户端库可将它识别为实体类型的特性。 此新类型仅包含特定地址属性以及标识属性。

Imports System
Imports System.Linq
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports ClientProjection.Northwind
Imports System.Data.Services.Client

Partial Public Class MainPage
    Inherits UserControl

    ' Create the binding collections and the data service context.
    Dim binding As DataServiceCollection(Of CustomerAddress)
    Dim context As NorthwindEntities
    Dim customerAddressViewSource As CollectionViewSource

    Public Sub Main()
        InitializeComponent()
    End Sub

    Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        'Instantiate the binding collection.
        binding = New DataServiceCollection(Of CustomerAddress)()

        ' Instantiate the context.
        context = _
            New NorthwindEntities(New Uri("https://localhost:54321/Northwind.svc/"))

        ' Define a handler for the LoadCompleted event of the collection.
        AddHandler binding.LoadCompleted, _
        AddressOf binding_LoadCompleted

        ' Define an anonymous LINQ query that projects the Customers type into 
        ' a CustomerAddress type that contains only address properties.
        Dim query = From c In context.Customers _
                            Where c.Country = "Germany" _
                            Select New CustomerAddress With
                                {.CustomerID = c.CustomerID, _
                                 .Address = c.Address, _
                                 .City = c.City, _
                                 .PostalCode = c.PostalCode, _
                                 .Country = c.Country _
                                }

        ' Execute the query asynchronously.
        binding.LoadAsync(query)
    End Sub

    Private Sub binding_LoadCompleted(ByVal sender As Object, ByVal e As LoadCompletedEventArgs)
        If e.Error Is Nothing Then
            ' Load all pages of Customers before binding.
            If Not binding.Continuation Is Nothing Then
                binding.LoadNextPartialSetAsync()
            Else
                ' Load your data here and assign the result to the CollectionViewSource.
                customerAddressViewSource =
                    CType(Me.Resources("customerAddressViewSource"), CollectionViewSource)
                customerAddressViewSource.Source = binding
            End If
        End If
    End Sub
    ' We need to persist the result of an operation 
    ' to be able to invoke the dispatcher.
    Private currentResult As IAsyncResult
    Private Sub saveChangesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Define the delegate to callback into the process
        Dim callback As AsyncCallback = AddressOf OnChangesSaved
        Try
            ' Start the saving changes operation. This needs to be a 
            ' batch operation in case we are added a new object with 
            ' a new relationship.
            context.BeginSaveChanges(SaveChangesOptions.Batch, _
                    callback, context)

        Catch ex As Exception
            MessageBox.Show(String.Format( _
                            "The changes could not be saved to the data service.\n" _
                            & "The following error occurred: {0}", ex.Message))
        End Try
    End Sub
    Private Sub OnChangesSaved(ByVal result As IAsyncResult)
        ' Persist the result for the delegate.
        currentResult = result

        ' Use the Dispatcher to ensure that the 
        ' asynchronous call returns in the correct thread.
        Dispatcher.BeginInvoke(AddressOf ChangesSavedByDispatcher)
    End Sub
    Private Sub ChangesSavedByDispatcher()

        Dim errorOccured As Boolean = False
        context = CType(currentResult.AsyncState, NorthwindEntities)

        Try
            ' Complete the save changes operation and display the response.
            Dim response As DataServiceResponse = _
            context.EndSaveChanges(currentResult)

            For Each changeResponse As ChangeOperationResponse In response
                If changeResponse.Error IsNot Nothing Then errorOccured = True
            Next
            If Not errorOccured Then
                MessageBox.Show("The changes have been saved to the data service.")
            Else
                MessageBox.Show("An error occured. One or more changes could not be saved.")
            End If
        Catch ex As Exception
            ' Display the error from the response.
            MessageBox.Show(String.Format("The following error occured: {0}", ex.Message))
        End Try
    End Sub
End Class
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using ClientProjection.Northwind;
using System.Data.Services.Client;

namespace ClientProjection
{
    public partial class MainPage : UserControl
    {
        // Create the binding collections and the data service context.
        private DataServiceCollection<CustomerAddress> binding;
        NorthwindEntities context;
        CollectionViewSource customerAddressViewSource;

        public MainPage()
        {
            InitializeComponent();
        }

        private void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            // Instantiate the binding collection.
            binding = new DataServiceCollection<CustomerAddress>();

            // Instantiate the context.
            context = 
                new NorthwindEntities(new Uri("https://localhost:12345/Northwind.svc/"));

            // Register the LoadCompleted event for the binding collection.
            binding.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(binding_LoadCompleted);

            // Define an anonymous LINQ query that projects the Customers type into 
            // a CustomerAddress type that contains only address properties.
            var query = from c in context.Customers
                        where c.Country == "Germany"
                        select new CustomerAddress
                        {
                            CustomerID = c.CustomerID,
                            Address = c.Address,
                            City = c.City,
                            PostalCode = c.PostalCode,
                            Country = c.Country
                        };

            // Execute the query asynchronously.
            binding.LoadAsync(query);
        }

        void binding_LoadCompleted(object sender, LoadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                // Load all pages of Customers before binding.
                if (binding.Continuation != null)
                {
                    binding.LoadNextPartialSetAsync();
                }
                else
                {
                    //Load your data here and assign the result to the CollectionViewSource.

                    customerAddressViewSource = 
                        (CollectionViewSource)this.Resources["customerAddressViewSource"];
                    customerAddressViewSource.Source = binding;
                }
            }
        }
        private void saveChangesButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Start the saving changes operation. 
                context.BeginSaveChanges(OnChangesSaved, context);
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("The changes could not be saved to the data service.\n"
                    + "The following error occurred: {0}", ex.Message));
            }
        }

        private void OnChangesSaved(IAsyncResult result)
        {
            bool errorOccured = false;

            // Use the Dispatcher to ensure that the 
            // asynchronous call returns in the correct thread.
            Dispatcher.BeginInvoke(() =>
            {
                context = result.AsyncState as NorthwindEntities;

                try
                {
                    // Complete the save changes operation and display the response.
                    DataServiceResponse response = context.EndSaveChanges(result);

                    foreach (ChangeOperationResponse changeResponse in response)
                    {
                        if (changeResponse.Error != null) errorOccured = true;
                    }
                    if (!errorOccured)
                    {
                        MessageBox.Show("The changes have been saved to the data service.");
                    }
                    else
                    {
                        MessageBox.Show("An error occured. One or more changes could not be saved.");
                    }
                }
                catch (Exception ex)
                {
                    // Display the error from the response.
                    MessageBox.Show(string.Format("The following error occured: {0}", ex.Message));
                }
            }
            );
        }
    }
}

下面的示例演示在上述示例中使用的 CustomerAddress 类型的定义。

[DataServiceKey("CustomerID")]
public partial class CustomerAddress : INotifyPropertyChanged
{
    private string _customerID;
    private string _address;
    private string _city;
    private string _postalCode;
    private string _country;

    public string CustomerID
    {
        get { return this._customerID; }
        set
        {
            this.OnCustomerIDChanging(value);
            this._customerID = value;
            this.OnCustomerIDChanged();
            this.OnPropertyChanged("CustomerID");
        }
    }
    public string Address
    {
        get { return this._address; }
        set
        {
            this.OnAddressChanging(value);
            this._address = value;
            this.OnAddressChanged();
            this.OnPropertyChanged("Address");
        }
    }
    public string City
    {
        get { return this._city; }
        set
        {
            this.OnCityChanging(value);
            this._city = value;
            this.OnCityChanged();
            this.OnPropertyChanged("City");
        }
    }
    public string PostalCode
    {
        get { return this._postalCode; }
        set
        {
            this.OnPostalCodeChanging(value);
            this._postalCode = value;
            this.OnPostalCodeChanged();
            this.OnPropertyChanged("PostalCode");
        }
    }
    public string Country
    {
        get { return this._country; }
        set
        {
            this.OnCountryChanging(value);
            this._country = value;
            this.OnCountryChanged();
            this.OnPropertyChanged("Country");
        }
    }
    #region INotifyPropertyChanged implementation
    // Members required by the INotifyPropertyChanged implementation.
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string property)
    {
        if ((this.PropertyChanged != null))
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
    partial void OnCustomerIDChanging(string value);
    partial void OnCustomerIDChanged();
    partial void OnAddressChanging(string value);
    partial void OnAddressChanged();
    partial void OnCityChanging(string value);
    partial void OnCityChanged();
    partial void OnRegionChanging(string value);
    partial void OnRegionChanged();
    partial void OnPostalCodeChanging(string value);
    partial void OnPostalCodeChanged();
    partial void OnCountryChanging(string value);
    partial void OnCountryChanged();
    #endregion
}

以下 XAML 定义了 Silverlight 应用程序的主页。

    <UserControl x:Class="ClientProjection.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="312" d:DesignWidth="577" 
             xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
             xmlns:my="clr-namespace:ClientProjection" Loaded="MainPage_Loaded">
    <UserControl.Resources>
        <CollectionViewSource x:Key="customerAddressViewSource" 
                              d:DesignSource="{d:DesignInstance my:CustomerAddress, CreateList=True}" />
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White" DataContext="" Height="312" Width="577"
          VerticalAlignment="Top" HorizontalAlignment="Left">
        <Grid.RowDefinitions>
            <RowDefinition Height="203*" />
            <RowDefinition Height="119*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="17*" />
            <ColumnDefinition Width="336*" />
        </Grid.ColumnDefinitions>
        <sdk:DataGrid AutoGenerateColumns="False" Height="233" HorizontalAlignment="Left" 
                      ItemsSource="{Binding Source={StaticResource customerAddressViewSource}}" 
                      Name="customerAddressDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" 
                      VerticalAlignment="Top" Width="553" Margin="12,24,0,0" 
                      Grid.RowSpan="2" Grid.ColumnSpan="2">
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding Path=CustomerID}" 
                                        Header="Customer" Width="80" />
                <sdk:DataGridTextColumn x:Name="addressColumn" Binding="{Binding Path=Address}" 
                                        Header="Address" Width="180" />
                <sdk:DataGridTextColumn x:Name="cityColumn" Binding="{Binding Path=City}" 
                                        Header="City" Width="120" />
                <sdk:DataGridTextColumn x:Name="countryColumn" Binding="{Binding Path=Country}" 
                                        Header="Country" Width="80" />
                <sdk:DataGridTextColumn x:Name="postalCodeColumn" Binding="{Binding Path=PostalCode}" 
                                        Header="Postal Code" Width="90" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
        <Button Content="Save" Grid.Column="1" HorizontalAlignment="Left" 
                Margin="462,78,0,0" Name="saveChangesButton" Width="75" Grid.Row="1" 
                Click="saveChangesButton_Click" Height="25" VerticalAlignment="Top" />
    </Grid>
</UserControl>

请参阅

其他资源

WCF Data Services Tasks for Silverlight