Archive for September, 2007

Enabling WCF Duplex Channel in DSA solution

One of the features included in Smart Client Software Factory  (May 2007) is the Disconnected Service Agent (DSA).

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. A Disconnected Service Agent is a service agent that provides offline capabilities; this is useful for occasionally connected applications.

The Smart Client Software Factory Source Code includes the Disconnected Service Agent (with CAB) QuickStart. This QuickStart uses a simple Windows Communication Foundation (WCF) service to retrieve a list of restaurants and menu items. When you click a restaurant on the Shell, the application uses a Disconnected Service Agent (DSA) to send requests to the WCF service, and displays the results (if the application is online).

Note: I recommend you to review the original QuickStart before going on reading. You can find information about the original QuickStart in the topic Inspecting the Software Factory Assets | QuickStars | Offline Application Blocks QuickStarts | Disconnected Service Agent (with CAB) QuickStart from the SCSF Help.

In this post I will show you how to update the DSA QuickStart to add support for a WCF Duplex Service to the Disconnected Service Agent.

A duplex service contract is a message exchange pattern in which both endpoints can send messages to the other independently. A duplex service, therefore, can send messages back to the client endpoint, providing event-like behavior. Duplex communication occurs when a client connects to a service and provides the service with a channel on which the service can send messages back to the client.

Restaurant Service

First, you must update the IMenuService service contract in order to specify in the ServiceContractAttribute the callback contract (using the CallbackContract property) that the client must implement in order to participate in a duplex conversation.

Also, in the OperationContractAttribute of the interface methods, we must specify IsOneWay = true property because the operations do not return reply messages.

[ServiceContract(CallbackContract = typeof(IMenuServiceCallback), SessionMode = SessionMode.Required)]
public interface IMenuService
{
    [OperationContract(IsOneWay = true)]
    void RequestMenuItems(string restaurantId);

    [OperationContract(IsOneWay = true)]
    void RequestRestaurants();
}

Then, we define the callback contract. The remote service will use this contract to send us the operation results.

public interface IMenuServiceCallback
{
    [OperationContract(IsOneWay = true)]
    void MenuItemsCallback(MenuItem[] items);
    [OperationContract(IsOneWay = true)]
    void RestaurantsCallback(Restaurant[] restaurants);
}

DuplexChannelFactory

The WCFProxyFactory included in the Microsoft.Practices.SmartClient.DisconnectedAgent assembly it doesn’t supports Duplex Channel. Because of that, we need to implement a new factory. Our factory will extend the existing WCFProxyFactory class and its name will be DuplexChannelFactory.

DSAwithDuplexChannelFactory

This is the signature for the DuplexChannelFactory class:

public class DuplexChannelFactory : WCFProxyFactory
where TChannel : class

In the factory, override the GetOnlineProxy method in order to provide the required functionality for Duplex Channel support. To make the factory generic, also add a new property that users will use to set the Callback type.

private static DuplexClientBase _onlineProxy;
private static Type _onlineProxyType;

// We are caching the proxy because it has to be alive
// to receive the callback
private static DuplexClientBase OnlineProxy
{
    get
    {
        if (_onlineProxy == null)
        {
            // To receive the callback, we have to specify the callback handler
            InstanceContext instanceContext = new InstanceContext(GetCallbackHandler());
            _onlineProxy = (DuplexClientBase)Activator.CreateInstance(_onlineProxyType, instanceContext);
        }

        return _onlineProxy;
    }
}

public override object GetOnlineProxy(Request request, string networkName)
{
    if (request == null)
        new ArgumentNullException("request");

    _onlineProxyType = request.OnlineProxyType;

    // Set the credentials
    ClientCredentials clientCredentials = OnlineProxy.ClientCredentials;

if ((EndpointCatalog != null) && (EndpointCatalog.Count > 0) && (EndpointCatalog.EndpointExists(request.Endpoint)))
    {
        NetworkCredential networkCredential = EndpointCatalog.GetCredentialForEndpoint(request.Endpoint, networkName);

        clientCredentials.UserName.UserName = networkCredential.UserName;
        clientCredentials.UserName.Password = networkCredential.Password;
        EndpointAddress address = new EndpointAddress(EndpointCatalog.GetAddressForEndpoint(request.Endpoint, networkName));
        OnlineProxy.Endpoint.Address = address;
    }

    return OnlineProxy;
}

private static Type _callback;

public static void SetCallbackHandler(Type callback)
{
    _callback = callback;
}

private static object GetCallbackHandler()
{
    if (_callbackInstance == null)
        _callbackInstance = Activator.CreateInstance(_callback);

    return _callbackInstance;
}

Quickstart Changes

The DSA Recipes generates a Callback class. In our custom implementation, this class implements the IMenuServiceCallback interface. This means that the remote service will invoke callbacks in this class.

public class Callback : CallbackBase, IMenuServiceCallback
{
    #region IMenuServiceCallback Members
    public void MenuItemsCallback(MenuItem[] items)
    {
        if (GetMenuItemsReturn != null)
        {
            GetMenuItemsReturn(this, new EventArgs(items));
        }

    } 

    public void RestaurantsCallback(Restaurant[] restaurants)
    {
        if (GetRestaurantsReturn != null)
        {
            GetRestaurantsReturn(this, new EventArgs(restaurants));
        } 

    }
    #endregion 

    //… 
}

To have the DSA block use our custom factory, you must modify the Agent class created by the Add Disconnected Service Agent as shown in the following code:

public static OfflineBehavior GetAgentDefaultBehavior()
{
    OfflineBehavior behavior = new OfflineBehavior();
    behavior.MaxRetries = 3;
    behavior.Stamps = 1;
    behavior.Expiration = DateTime.Now + new TimeSpan(1, 0, 0, 0);
    behavior.ProxyFactoryType = typeof(DuplexChannelFactory);
    return behavior;
}

Finally, in the RestaurantModule class, set the callback type for the factory. You set the callback type by invoking the SetCallbackHandler method on the factory as shown below.

private void AddServices()
{
    DuplexChannelFactory.SetCallbackHandler(typeof(Callback));
    WorkItem.Services.AddNew();
}

You are ready to run the QuickStart. With the changes made, the client sends requests to the WCF service but it does not wait for the service responses; instead, the remote service invokes callbacks in an instance of the Callback class (which implements the callback contract, IMenuServiceCallback) when the operations are completed.

Important: The code available for download is provided "as is" with no warranties of any kind.

Attachment(s): MyCustomSessionState.zip

Smart Client Software Factory and Web Client Software Factory Knowledge Bases published!

We recently released the Smart Client Software Factory Knowledge Base and the Web Client Software Factory Knowledge Base.

The purpose of these Knowledge Bases is categorize and organize blogs posts, CodePlex pages, CodePlex posts, etc that can be valuable for the community. With this kind of information organized the community will have a new place to search for help/guidance.

 

Smart Client Software Factory Knowledge Base

Web Client Software Factory Knowledge Base

Web Client Software Factory (June 2007) - Hands on Labs Release!

Specifically, you will learn how to perform the following tasks:

  • Create a new Web client solution.
  • Create a business module.
  • Expose a business module to the Web site shell.
  • Create a Composite Web Application Block service.
  • Create a view with a presenter and unit tests.
  • Use unit testing for pages with data bound controls.
  • Define role-based access to a module’s features.
  • Configure exception handling and logging.
  • Define and execute a page flow.

Download

Smart Client Software Factory & Visual Studio 2008 Beta 2 - Workarounds for Visual Basic Known Issues

NOTE: A new version of Smart Client Software Factory was published. This new version is for Visual Studio 2008. Please check this post for more information.

Now that we have SCSF installed in Orcas B2, we are able to apply workarounds for the known issues (Visual Basic).

Note: For C# know issues, click here.

> Symptom: When adding a WPF view with presenter, Visual Studio crashes.

OrcasB2-issuesVB_1

Problem
We believe that the problem has to do with the Target Framework as in C#, but we are not sure.

Workarounds

  • Workaround 1: Update manually in the project’s properties page the Target Framework, setting it to 3.0 or 3.5 (this would have to be done before adding a WPF View so that it doesn’t crash)

OrcasB2-issuesVB_1
Fig. 1 | Target Framework in VB.NET project

OrcasB2-issuesVB_3
Fig. 2 | Module.vbproj.template

> Symptom: Form1 is not a member of Shell

OrcasB2-issuesVB_4
Fig. 3 | Error Message : ‘Form1’ is not member of Shell

Problem
The MainForm set to the project is Form1 instead of ShellForm.

Workarounds

  • Workaround 1: Manually, by replacing the “Form1” with a “ShellForm

OrcasB2-issuesVB_5
Fig. 4 | Application.Designer.vb

OrcasB2-issuesVB_6
Fig. 5 | Application.myapp.template

> Symptom: After adding a WPF View and compile the solution, a warning appears.

OrcasB2-issuesVB_7

Problem
The file which contains the WinFx targets is being imported more than once.

So far we were only able to fix this manually. However, we could search for a way to make the Guidance Package get the environment (whether it’s running Orcas Beta2 or not) and, in case it’s running Orcas B2, skip adding the import line.

OrcasB2-issuesVB_8

Smart Client Software Factory & Visual Studio 2008 Beta 2 - Workarounds for C# Known Issues

NOTE: A new version of Smart Client Software Factory was published. This new version is for Visual Studio 2008. Please check this post for more information.

Now that we have SCSF installed in Orcas B2, we are able to apply workarounds for the known issues (C#).

Note: For Visual Basic know issues, click here. 


> Symptom: When you add a WPF view with presenter, a compilation error occurs because some references are missing.

OrcasB2-issuesCsharp_1

Problem

The Target Framework for the unfolded project is 2.0 by default. With this target framework, references to .NET 3.0 assemblies are not being added by Visual Studio.

Workarounds

  • Workaround 1: Update manually in the project’s properties page the Target Framework, setting it to 3.0 or 3.5

OrcasB2-issuesCsharp_2
Fig. 1 | Target Framework in C# project

OrcasB2-issuesCsharp_3
Fig. 2 | Module.csproj.template

> Symptom: After adding a WPF View and compile the solution, a warning appears.

OrcasB2-issuesCsharp_4

Problem
The file which contains the WinFx targets is being imported more than once.

So far we were only able to fix this manually. However, we could search for a way to make the Guidance Package get the environment (whether it’s running Orcas Beta2 or not) and, in case it’s running Orcas B2, skip adding the import line.

OrcasB2-issuesCsharp_5