Saturday, January 30, 2010

The "Fancy Proxy" - having fun with WCF - 4-MSMQ Duplex

This article is part of The "Fancy Proxy" tutorial - implementing a few advance ideas in WCF in one solution.

This is the 4th step - MSMQ Duplex.

Ok...so let's see what we explored till now:
In the first two steps we saw simple TCP & simple MSMQ samples on the 3rd we experienced a sample a bit more complex - duplex.

Duplex allowed us to use two "one-way" interfaces to build a dialog between the client & the server in duplex the client actually leaves his endpoint and "becomes" the server waiting for the real one to call him back...
This is nice, but it's not quite the disconnected solution we are looking for.
MSMQ is, but then the "online" code would be weird & not natural using only one-way communication.

So what we're really looking for is a way to work with MSMQ but not only one-way but both-ways - meaning some kind of combination between MSMQ & Duplex.
Looking for this type of solution I was amazed to discover Microsoft did not implemented a "two-way" solution for MSMQ...All the samples I found included use of old MSMQ libraries code...opening queues etc..that's not WCF...WCF is suppose to separate between the code & the transport decisions....hmmm...

Then I found a great work by Nicolas Dorier called Duplex MSMQ I won't get deep here on how he implemented this if you want to read about it better go to the source on Code Project.
So let's take this excellent solution for a spin..

First we open a queue for the server & for the client since they will both talk to each other using queues (in develop environment it's on the same machine, in production it will of course be separated).




The contract is similar to duplex solution, it includes two "one-way" interfaces when the second acts as the "callback" interface of the first one.


[ServiceContract(CallbackContract=typeof(ISampleContractCallback))]
public interface ISampleContract
{
[OperationContract(IsOneWay=true)]
void GetData(Guid identifier);

[OperationContract(IsOneWay = true)]
void Execute(Guid identifier);
}

public interface ISampleContractCallback
{
[OperationContract(IsOneWay = true)]
void SendData(Guid identifier, string answer);
}


Same on the server's code - looks like regular duplex solution, notice the 'GetCallbackChannel' that retrieves the channel back to the client.


public void GetData(Guid identifier)
{
Console.WriteLine("recieved GetData request (id {0})", identifier);

ISampleContractCallback client = OperationContext.Current.GetCallbackChannel();

//get data by identifier
string answer = "hi! from server";

client.SendData(identifier, answer);
}

public void Execute(Guid identifier)
{
Console.WriteLine("recieved Execute request (id {0})", identifier);
}


The host is the same as previous steps...

On the client's code there are some changes, the proxy gets the channel from the 'DuplexMsmqChannelFactory' (part of the duplex-msmq solution) and it implements both interfaces, I showed here one way to implement this using events though it's not part of our final solution.


public class testFancyProxyProxy:ISampleContract, ISampleContractCallback
{
private ISampleContract proxy;

public delegate void AnswerArrivedHandler(object sender, AnswerArrivedArgs args);

public event AnswerArrivedHandler AnswerArrived;

public testFancyProxyProxy()
{
proxy = DuplexMsmqChannelFactory.GetChannel(ConfigurationSettings.AppSettings["ClientURI"],
this,
ConfigurationSettings.AppSettings["ServerURI"]);
}

#region ISampleContract Members

public void GetData(Guid identifier)
{
proxy.GetData(Guid.NewGuid());
}

public void Execute(Guid identifier)
{
proxy.Execute(Guid.NewGuid());
}

#endregion

#region ISampleContractCallback Members

public void SendData(Guid identifier, string answer)
{
OnAnswerArrived(this, new AnswerArrivedArgs(identifier, answer));
}

#endregion

protected void OnAnswerArrived(object sender, AnswerArrivedArgs args)
{
if (AnswerArrived != null)
{
AnswerArrived(sender, args);
}
}
}

public class AnswerArrivedArgs:EventArgs
{
private Guid _identifier;
private string _answer;

public AnswerArrivedArgs(Guid identifier, string answer)
{
_identifier = identifier;
_answer = answer;
}

public Guid Identifier { get { return _identifier; } }
public string Answer { get { return _answer; } }
}


And the consumer, simple test:


public void Run()
{
Console.WriteLine("press any key to run..");
Console.Read();

proxy = new testFancyProxyProxy();

proxy.AnswerArrived += new testFancyProxyProxy.AnswerArrivedHandler(proxy_AnswerArrived);

proxy.GetData(Guid.NewGuid());

proxy.Execute(Guid.NewGuid());

Console.WriteLine("press any key to close client..");
Console.Read();
}

private void proxy_AnswerArrived(object sender, AnswerArrivedArgs args)
{
Console.WriteLine("answer from server for id {0}: {1}", args.Identifier, args.Answer);
}




That's it for this step.

2 more steps to go...in the next one we will see a nice way to wrap & encapsulate the changing of the proxy (online/offline) using dynamic proxy & than we will combine all we learned here together in the final step.

Till than...

Feel free to ask/comment...

Diego

PS: Source download

5 comments:

  1. very useful post- thanks a lot!!!

    ReplyDelete
  2. Great article! the whole series is very useful and is exactly what I have been looking for in reference to guidence on refactoring an old application.

    On this particular article you reference DuplexMsmqChannelFactory. I have looked over the original CodeProject.com code and the codeplex code. I cannot find a reference to this. Is this something that you have created based on the DuplexMSMQ project or another (older) version of the project?

    thanks.

    Jt
    jtolar [at] flexcorp com

    ReplyDelete
  3. Hi Jt

    1st of all nice to hear the article helped you
    if you modify or improve it in any way - do tell...

    As for that class I think it's a short helper class
    I've added to MSMQDuplex library.
    You can download the sample code attached
    to this article & take it from there.

    Have fun
    Diego

    ReplyDelete
  4. missing reference in server project called DuplexMsmq

    ReplyDelete
  5. It's good article .But it seems your attachment is missed the reference DuplexMSMQ.

    Could you please help ?I need this reference to run

    Thanks

    ReplyDelete