How to: Create a Disconnected Service Agent

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies.
This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
To create client business applications using current Microsoft technologies, see patterns & practices' Prism.

A service agent provides an abstraction of a service (typically a Web service) and implements a set of features and capabilities that make it convenient and effective to use the service. Architects and developers use service agents to extend the functionality provided by the platform-generated service proxies. A disconnected service agent is a service agent that provides offline capabilities; this is useful for occasionally connected applications.

The Smart Client Software Factory guidance package includes a recipe named Create Disconnected Service Agent. This recipe automates the creation of a disconnected service agent that consumes the Disconnected Service Agent Application Block to send requests to services.

The Create Disconnected Service Agent recipe assumes that you have an existing software factory smart client solution with a Web reference (for ASMX), service reference (for Windows Communication Foundation [WCF]), or a local class for which you want to create a disconnected service agent. For information about how to create a smart client solution, see How to: Create Smart Client Solutions.

Ff699190.note(en-us,PandP.10).gifNote:
Note: This recipe requires a solution structure that is created by the software factory when you unfold the Smart Client Application template.

The following procedure describes how to use the recipe to create a disconnected service agent. You can create a service agent for an ASMX Web service, a WCF Web service, or any kind of service (local or remote).

Ff699190.note(en-us,PandP.10).gifNote:
Note: If you run the Create Disconnected Service Agent recipe more than one time on the same Web (or service) reference, the recipe appears to run, but it does not create new files or modify the existing files. (This is also true for different Web references that specify the same type.) To update an existing disconnected service agent, run the Update Disconnected Service Agent recipe instead.

To create a disconnected service agent

  1. Using Visual Studio, open an existing smart client solution.
  2. Build the solution.
    Ff699190.note(en-us,PandP.10).gifNote:
    Note: The Create Disconnected Service Agent recipe uses reflection and requires the solution to be compiled.

  3. There are three ways to run the recipe. Steps 4–6 describe the different ways you can run the recipe.
  4. For a Web reference to an ASMX service: In Solution Explorer, right-click a Web reference, point to Smart Client Factory, and then click Create Disconnected Service Agent, as shown in Figure 1.

    Ff699190.68280eb2-7f6c-40b2-ba74-a9a8e1d5a4cd(en-us,PandP.10).png

    Figure 1

    The Create Disconnected Service Agent recipe menu for a Web reference
  5. For a service reference to a WCF or ASMX service, in Solution Explorer, right-click a Service reference file, point to Smart Client Factory, and then click Create Disconnected Service Agent, as shown in Figure 2.

    Ff699190.6b67bd85-caa2-436b-ba58-e0ff8ea76def(en-us,PandP.10).png

    Figure 2

    The Create Disconnected Service Agent recipe menu for a service reference
  6. To add a disconnected service agent for a class in a module, in Solution Explorer, right-click a project node, point to Smart Client Factory, and then click Create Disconnected Service Agent, as shown in Figure 3.

    Ff699190.d84fc146-dc6d-4733-9fa1-54f81b00ff9d(en-us,PandP.10).png

    Figure 3

    The Create Disconnected Service Agent recipe menu
  7. The Create Disconnected Service Agent Wizard appears. If you run the recipe by selecting a Web reference or service reference in Solution Explorer, the proxy type field will be set by default, and you can skip the next step.
  8. Click the ellipsis next to the Proxy Type text box to browse for the class that implements the service for which you will create the disconnected service agent. The Class Browser will display the types contained in your solution, as shown in Figure 4.

    Ff699190.5d46aea0-0679-454c-88c4-0d1eb8d108e7(en-us,PandP.10).png

    Figure 4

    Using the Class Browser to locate the service type
  9. In the Class Browser, select the class that represents the client interface (this can be any class, including a Web service proxy class or service reference proxy class) for the service implementation, and then click Accept.
    Ff699190.note(en-us,PandP.10).gifNote:
    If you do not see the assembly that contains the class you want to select as the client interface, save your project, close and restart Visual Studio, and then run the recipe again.

  10. If you selected a Web reference or service reference, the Proxy Type field and the Type methods list are populated with default values, as illustrated in Figure 5.

    Ff699190.a0bb27ec-a1fc-4b0e-90cd-ac70ab80a8d7(en-us,PandP.10).png

    Figure 5

    Create Disconnected Service Agent wizard with default values
  11. Click the Finish button.

Advanced Options

The Create Disconnected Service provides additional options that you can use for advance configuration. To see these options, click the Advanced tab in the wizard. You will see a screen similar to the one illustrated in Figure 6.

Ff699190.d266a8ae-8b1e-4ea9-8f43-2869018ed4f3(en-us,PandP.10).png

Figure 6

Advanced options

You can customize the following fields in the Advanced tab:

  • Endpoint. You can enter the name of the endpoint that you want to use for this Web service. If you enter an endpoint name, the recipe will add the endpoint definition to the application configuration file (if necessary, it will create a configuration section for the Endpoint Catalog Application Block). If you do not enter an endpoint name, the disconnected agent will not use the Endpoint Configuration Application Block to manage the endpoint. Instead, it will use the endpoint address defined in the Web (or service) proxy class.
  • Stamps. You can enter a value for the stamps, which sets the importance of the request. If you use the Price attribute to configure connections, the Disconnected Service Agent Application Block will only dispatch messages with a stamp value that is greater than, or equal to, the connection price. (For information about how to configure connections, see Configuring the Connection Monitor Application Block.) By default, all messages sent with the disconnected service agent will have the same stamp value. If you want to change this value for a specific request, you can modify the Agent class for the disconnected service agent (the recipe generates a method for each operation that you can use to easily customize the behavior per operation).
  • Max Retries. This specifies the maximum number of times that the disconnected service agent will attempt to send a message before the message is placed into the dead letter queue.
  • Expiration. The expiration time specifies the maximum amount of time a message will remain in the queue when offline. After this time, the message is deleted.
  • Tag. The disconnected service agent uses the tag value to identify and filter requests.
  • Proxy factory type. A proxy factory is a component that is used by the Disconnected Service Agent Application Block to retrieve the online proxy instance and invoke its methods. Select the proxy factory type corresponding to your service:
    • WebServiceProxyFactory. This instantiates proxies that interact with ASMX Web services (they must inherit from the System.Web.Services.Protocols.SoapHttpClientProtocol class). Visual Studio generates SoapHttpClientProtocol-derived classes when you use the Add Web Reference dialog box.
    • WCFProxyFactory. This instantiates proxies that interact with Windows Communication Foundation Web services (they must inherit from the System.ServiceModel.ClientBase<T> class).
    • ObjectProxyFactory. This instantiates generic proxies (any object).

The recipe creates three class files within your application. These files provide the proxy class, the corresponding set of callback handlers you need for making calls to the Web service, and an abstract base class that defines the callback methods. The Disconnected Service Agent, implemented within the Disconnected Service Agent application block, can use these files to simplify the code you have to write.

Figure 7 illustrates the solution with the generated class files.

Ff699190.f3a8ae34-2547-4b62-beb3-b71fa6ac0798(en-us,PandP.10).png

Figure 7

Solution with a disconnected service agent generated by the recipe

All the files use a namespace generated from the project default namespace and the name you specified for the Web service proxy. The namespace uses the following format.

[projectnamespace].DSA.[servicename]

The classes for all disconnected service agents use the same names (each disconnected service agent is contained with a unique namespace.)

The recipe creates the following three files:

  • Agent.cs. This file contains the code that defines the offline behavior. You can customize this code to modify per operation behavior or adjust the default values that you entered in the wizard when you ran the recipe.
  • Callback.cs. This file is where you write the business logic that responds to the Web service responses. This file derives from the disconnected service agent's CallbackBase class.
  • CallbackBase.cs. This file contains an abstract base class with methods that match the Web service operations you selected in the recipe wizard.
Ff699190.note(en-us,PandP.10).gifNote:
Note: The CallbackBase file is dependent on the Callback concrete class. To see this file in Solution Explorer, you might need to click Show All Files on the Project menu. The abstract base class appears under the concrete callback class.

Web Service Proxy File

The Web service proxy file generated by the recipe implements a public constructor and two public overloads for each method that you selected on the first page of the recipe wizard.

The constructor in the proxy takes a reference to a request queue and stores it in a class-level variable.

IRequestQueue requestQueue;

public Agent(IRequestQueue requestQueue)
{
    this.requestQueue = requestQueue;
}

The two public method overloads for each Web service method provide simple abstractions that, underneath, call the Web service methods. You can use the GUID that the proxy class methods return to get a reference to the queued request. The first of these two method overloads takes only the parameters required by the Web service.

public Guid HelloWorld(String message)
{
    return HelloWorld(message, GetHelloWorldDefaultBehavior());
}

You can see that it uses a private method named GetHelloWorldDefaultBehavior (the name of the method prefixed with "Get" and suffixed with "DefaultBehavior"). This method, implemented within the proxy class, generates a new OfflineBehavior instance and populates its properties with the values you specified in the recipe for this Web service method.

The HelloWorld overload shown in the preceding code then calls the second public overload for this Web service method, specifying the parameters required by the Web service and the new OfflineBehavior instance.

public Guid HelloWorld(String message, OfflineBehavior behavior)
{
    behavior.ReturnCallback = new CommandCallback(typeof(Callback), "OnHelloWorldReturn");
    behavior.ExceptionCallback = new CommandCallback(typeof(Callback), "OnHelloWorldException");

    return EnqueueRequest("HelloWorld", behavior, message);
}

You can see that this overload first adds the two required callback handler references to the OfflineBehavior. The callback handlers use types specified in the other of the two files generated by the recipe, the callback handlers file, and generates names for these callback handlers using the name of the Web service method.

Ff699190.note(en-us,PandP.10).gifNote:
Note: Because this second overload is public, you can call the Web service method with a custom OfflineBehavior instance if required. This may be the case if you want to use a different stamps value or timeout for a specific request.

Finally, the method calls another private method in this class to enqueue the request, passing to it the name of the Web service method, the populated OfflineBehavior instance, and the value of the parameter required by the Web service method.

The private EnqueueRequest method in the proxy class file creates a new Request instance and (through another private method named CreateRequest) sets its properties using the name of the Web service method, the OfflineBehavior instance, and the parameters for the Web service method. It also sets the OnlineProxyType and specifies the Endpoint with the values you entered into the first page of the recipe wizard before calling the Enqueue method of the queue you specified in the constructor.

private Guid EnqueueRequest(string methodName, OfflineBehavior behavior, params object[] arguments)
{
    Request request = CreateRequest(methodName, behavior, arguments);

    requestQueue.Enqueue(request);

    return request.RequestId;
}

private Request CreateRequest(string methodName, OfflineBehavior behavior, params object[] arguments)
{
    Request request = new Request();
    request.MethodName = methodName;
    request.Behavior = behavior;
    request.CallParameters = arguments;

    request.OnlineProxyType = typeof(RootNamespace.Module1.MyAsmxWebService.Service);
    request.Endpoint = "MyAsmxWebService";

    return request;
}

Callback Handlers File

In most cases, you will want to handle the events that occur when a queued request succeeds or fails after it is dispatched to the remote service. The other two files generated by the recipe define and declare the callback handlers specified in the proxy class when it sets the ReturnCallback and ExceptionCallback properties of the OfflineBehavior:

  • An abstract base class contains abstract declarations of the callback methods. This makes it easier to ensure that your custom callback class exposes the correct methods. The Update Disconnected Service Agent recipe regenerates this file.
  • A concrete class that inherits from the abstract base class contains the outlines for the methods that you must implement. Because this class contains your custom logic for handling calls to the Web service, the Update Disconnected Service Agent recipe preserves its contents.

Of course, the recipe can only generate outlines for these callback methods, because it cannot determine what actions you want to take when the events occur. Instead, it generates throw statements that indicate you must implement the required code (for example, to update the display or show an error message).

public class Callback : CallbackBase
{
    #region HelloWorld

    public override void OnHelloWorldReturn(Request request, object[] parameters, String returnValue)
    {
        throw new NotImplementedException();
    }

    public override OnExceptionAction OnHelloWorldException(Request request, Exception ex)
    {
        throw new NotImplementedException("Not implemented", ex);
    }

    #endregion HelloWorld
}
Ff699190.note(en-us,PandP.10).gifNote:
Note: The signature of the callback methods depends on the signature of the Web service methods. In this example, the HelloWorld method of the Web service returns a string, which you can see in the method signature in the preceding code.

The following are typical tasks that you perform after you create a disconnected service agent:

  • Add code for callback methods. Callback methods are called when a request is dispatched. Write application-specific code to handle the callbacks.
  • Customize the service agent code. For example, you could write code to translate entities inside the service agent. To do this, you can use the EntityTranslatorService. For more information, see How to: Translate Between Business Entities and Service Entities.
  • Register the service agent with a WorkItem. For more information, see Adding Services.

For more information about how to consume a disconnected service agent, see How to: Consume a Disconnected Service Agent.



Show: