Saturday, May 28, 2011

WCF Duplex Message Exchange Pattern - Simple sample

This post just explains how to create a simple WCF service of Duplex MEP(Message Exchange Pattern).
  • Two Service contracts need to be created.
  • First, create a Callback contract which the client will implemnt. The service will use this Callback contract to call the client back.
  • Then as for other WCF services create a Service contract and associate the Callback contract to this Service contract using the CallbackContract property of the Service contract attribute.
  • Specify the Operation contracts as "OneWay".
  • Session is required(enabled by default).
  • Get a channel to the current client instance using OperationContext.Current.GetCallbackChannel<>() to call the callback methods.
  • Configure the service with the wsDualHttpBinding.
Snippet 1 - Service Contract

namespace DuplexWcfService1
{
    [ServiceContract]
    public interface IAddHandler
    {
        [OperationContract(IsOneWay = true)]
        void Resultant(double result);
    }

    [ServiceContract(Namespace = "DuplexWcfService1", CallbackContract = typeof(IAddHandler))]    
    public interface IAddService
    {
        [OperationContract(IsOneWay = true)]
        void Add(double param1, double param2);
    }
}

Snippet 2 - Service Implementation

namespace DuplexWcfService1
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class AddService : IAddService
    {
        public void Add(double param1, double param2)
        {
            double result = param1 + param2;

            IAddHandler handler = OperationContext.Current.GetCallbackChannel<IAddHandler>();
            handler.Resultant(result);
        }
    }
}

Snippet 3 - Configuration

<system.serviceModel>
  <services>
    <service name="DuplexWcfService1.AddService">
      <endpoint address="AddService" binding="wsDualHttpBinding" contract="DuplexWcfService1.IAddService" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:2290/"/>
        </baseAddresses>
      </host>        
    </service>
  </services>
  
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
        <serviceMetadata httpGetEnabled="true"/>
        <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

I've posted the above sample snippets with namespace for valid reasons. In my configuration I've specified the "name" property of the "service" element as fully qualified name of my service implementation.

If I failed to do so, like if I mentioned "AddService" instead of "DuplexWcfService1.AddService" then, the error "Contract requires Duplex, but Binding 'BasicHttpBinding' doesn't support it or isn't configured properly to support it." will be raised.
Hence be cautious on specifying the values for properties in the configuration.

Now create a console application for consuming the above created service. For that,
  • First add the service reference to the service.
  • Create an implementation for the Callback contract.
  • Call the operation to infer the Duplex MEP.
Snippet 4 - Callback implementation

public class AddHandler : AddServiceServiceReference1.IAddServiceCallback
{

    public void Resultant(double result)
    {
        Console.WriteLine("add result : " + result.ToString());            
    }
}

Snippet 5 - Consuming the service

class Program
{
    static void Main(string[] args)
    {
        InstanceContext instanceContext = new InstanceContext(new AddHandler());
        AddServiceServiceReference1.AddServiceClient serviceClient = new AddServiceServiceReference1.AddServiceClient(instanceContext);

        serviceClient.Add(10, 90);
        Console.ReadKey();
    }
}

In order to understand the implementation easily, I've used primitive data types. Use custom types and scale up the scenario when you try on your own. 

Thursday, May 26, 2011

Silverlight 4 - Bind to a property in the level of parent or Ancestor

In Silverlight, most of us will use the MVVM pattern for which we will create a ViewModel and bind it to the respective View.

In such cases, there might arise a scenario in which we need to bind a property for an element or control which is embeded inside another control. Let us take a real case scenario in order to understand the problem clearer.
Consider that our ViewModel contains two properties (CustomCollection, ButtonVisibility) of which one of type ObservableCollection(CustomCollection) and other one of type Visibility(ButtonVisibility).

Now in the View, first we need to bind the ObservableCollection to a ListBox as follows.

Snippet 1

<Grid x:Name="LayoutRoot" Background="White">
    <ListBox ItemsSource="{Binding Path=CustomCollection}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Path=CustomName}" />
                    <Button Content="Sample Button"></Button>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>    
</Grid>

Then if i want to set the ButtonVisibility property to the button control inside the DataTemplate of the ListBox, then there should be some way to specify that the property belongs to the ancestor and not the current. Unfortunately we can't directly specify this in Silverlight 4.

In order to achieve this we can do an alternate by using ElementName property of Binding. In MVVM, we will be assigning the ViewModel's instance to the View's DataContext. Hence any root level elemnt can be made use. In our case I'll take the "LayoutRoot" element(which is created by default).

Snippet 2

<Grid x:Name="LayoutRoot" Background="White">
    <ListBox ItemsSource="{Binding Path=CustomCollection}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Path=CustomName}" />
                    <Button Content="Sample Button" Visibility="{Binding Path=DataContext.ButtonVisibility, ElementName=LayoutRoot}"></Button>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

You can use any valid element name with relevant DataContext you need for the property to bind.
Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.