DomainContext and Operations

[ This document was written for WCF Services Version 1 Service Pack 2 and might not be up to date Please see Release Notes or Changelog for a list of changes since WCF RIA Services ]

This topic describes how a client of a Open Ria Services application uses the domain context to communicate with the domain service in the application middle tier. You do not interact directly with domain services from within the client project. Instead, a domain context class is generated in the client project for every domain service in the server project. You call methods on the domain context class that correspond to the domain service method you want to use. The generated domain context class derives from the DomainContext class. By default, the domain context is named with a postfix of Context instead of the postfix of Service that is used to name the domain service. For example, a domain service named HRService has a corresponding domain context named HRContext. For information about defining domain service methods, see Domain Services.

The generated domain context contains three constructor methods:

  1. A default constructor that embeds the URI for communicating with the domain service over HTTP.

  2. A constructor that enables you to specify an alternate URI.

  3. A constructor that enables you to provide a custom DomainClient implementation. This constructor is typically used for unit testing or for redirecting to a custom transport layer.

The DomainContext class supports query, submit and invoke domain operations. For each type of operation, there is a corresponding class that represents the in-progress asynchronous operation. These classes are: LoadOperation, SubmitOperation, and InvokeOperation.

An EntitySet object is generated in the domain context with properties that indicate which operations (insert, update, or delete) are permitted from the client.

Queries

The query method in the domain context typically has the same name as the domain service query method plus a postfix of Query. For example, a GetCustomersQuery method in the domain context is generated from a GetCustomers method in the domain service. The domain context query method returns an EntityQuery object that you can use to apply additional operations.

All queries from a domain context are executed asynchronously. To execute the query, you pass the EntityQuery object as a parameter in the Load method.

For more information, see Walkthrough: Retrieving and Displaying Data From a Domain Service.

The following code shows how to retrieve customers from the domain service. It filters customers who have phone numbers that start with 583 and orders them alphabetically by LastName. The results are displayed in a DataGrid.

Partial Public Class MainPage
    Inherits UserControl

    Private _customerContext As New CustomerDomainContext

    Public Sub New()
        InitializeComponent()

        Dim query As EntityQuery(Of Customer)

        query = _
            From c In Me._customerContext.GetCustomersQuery() _
            Where c.Phone.StartsWith("583") _
            Order By c.LastName

        Dim loadOp = Me._customerContext.Load(query)
        CustomerGrid.ItemsSource = loadOp.Entities
    End Sub

End Class
public partial class MainPage : UserControl
{
    private CustomerDomainContext _customerContext = new CustomerDomainContext();

    public MainPage()
    {
        InitializeComponent();
        EntityQuery<Customer> query = 
            from c in _customerContext.GetCustomersQuery()
            where c.Phone.StartsWith("583")
            orderby c.LastName
            select c;
        LoadOperation<Customer> loadOp = this._customerContext.Load(query);
        CustomerGrid.ItemsSource = loadOp.Entities;
    }
}

Modifying Data

When the domain service includes methods for updating, inserting, and deleting entities, those methods are not generated in the domain context. Instead, you use the SubmitChanges method on the domain context and the proper operations on the domain service are called. No changes in the data source are made until you call SubmitChanges. You can cancel pending changes by calling the RejectChanges method.

The DomainContext class also provides the HasChanges and EntityContainer properties to enable you to evaluate pending changes. The EntityContainer object for a domain context tracks the pending changes. Pending changes does not include calls to invoke operations in the domain service because invoke operations are executed immediately when they are called. When you call SubmitChanges, all pending changes are sent to the domain service together.

You can add a new entity by calling either the Add method on the EntitySet\ object or by calling the AddNew method on the IEditableCollectionView object. When you add an entity by calling the AddNew method, that entity is rendered in the collection before the new entity is saved in the data source.

For more information, see Walkthrough: Editing Data From a Domain Service.

Named Update Methods

The domain context will contain a method for each named update method on the domain service that has a public access modifier and is not marked with the IgnoreAttribute attribute. The generated method in the domain context contains the same name as the method on the domain service. In the client project, you call the method, but the method is not actually executed until SubmitChanges is called. The EntityContainer tracks all calls to named update methods as pending changes. When you call SubmitChanges the method is processed asynchronously.

The same named update method is also generated in the client project for the entity that is passed as a parameter in the named update method. Therefore, the named update method can be called through either an instance of the domain context or an instance of the entity. If you open the generated code file, you will notice that the generated method in the domain context simply calls the generated method in the entity. In either case, you must still call the SubmitChanges method on the domain context to execute the method.

The following example shows how to call a named update method named ResetPassword on the entity. OnSubmitCompleted is a callback method that you implement to handle the results of the data operation.

selectedCustomer.ResetPassword()
customerContext.SubmitChanges(AddressOf OnSubmitCompleted, Nothing)
selectedCustomer.ResetPassword();
customerContext.SubmitChanges(OnSubmitCompleted, null);

Invoke Operations

The domain context will contain a method for each invoke operation on the domain service. Unlike domain operations, invoke operations are executed immediately. You do not call the SubmitChanges method. Invoke operations are executed asynchronously. The invoke operation returns an InvokeOperation object. You retrieve the value of the Value property to get the returned value from the service operation.

In almost all scenarios, you should use query operations instead of invoke operations to load data. Query methods return either a single Entity object, an IQueryable\ object, or an IEnumerable\ object. Query methods are an integral part of the data pattern supported by DomainService on the middle-tier and by DomainContext on the client. The Open Ria Services framework generates entities in the client project for only those entities that are returned from query methods in a DomainService.

Invoke operations provide an out-of-band mechanism for returning non-entity data and executing operations with side-effects. For more information about side-effects, see the HasSideEffects property. Invoke operations are usually not appropriate for query methods. Even when an invoke operation returns an entity, the entity is generated for the client project only if it is returned by a query method.

The following example shows how to use an invoke operation named GetLocalTemperature. For this example, assume that the method retrieves a value that is not related to any entity data.

Dim invokeOp As InvokeOperation(Of Integer)
invokeOp = customerContext.GetLocalTemperature(selectedPostalCode, AddressOf OnInvokeCompleted, Nothing)

Private Sub OnInvokeCompleted(ByVal invOp As InvokeOperation(Of Integer))
  If (invOp.HasError) Then
    MessageBox.Show(String.Format("Method Failed: {0}", invOp.Error.Message))
    invOp.MarkErrorAsHandled()
  Else
    result = invOp.Value
  End If
End Sub
InvokeOperation<int> invokeOp = customerContext.GetLocalTemperature(selectedPostalCode, OnInvokeCompleted, null);

private void OnInvokeCompleted(InvokeOperation<int> invOp)
{
  if (invOp.HasError)
  {
    MessageBox.Show(string.Format("Method Failed: {0}", invOp.Error.Message));
    invOp.MarkErrorAsHandled();
  }
  else
  {
    result = invOp.Value;
  }
}

Handling Errors

When you retrieve or modify data, you must decide how to handle errors that might arise from these operations. When you call methods on the domain context that retrieve or modify data, you include parameters that specify the steps for handling errors. When loading data, you can specify that errors are ignored. When modifying data, you must handle the exception that is returned. For more information, see Error Handling on the Client.

Last updated