Downlod the binary package from http://sourceforge.net/projects/asyncwcflib and open Test2.ClientAsyncAwait project to find code on this page.
For the following environments solution and projectfiles are provided:
See [AsyncWcfLib package diagram] for an overview.
To serialize objects, WCF must know all classes intended to be messages (classes that have a [DataContract] attribute).
Assembly "Test2.Messages" provides a method AddKnownMessageType. It registers all messages implemented by the user of AsyncWcfLib:
Test2Rsp.AddKnownMessageTypes();
To send messages you need an ActorOutput. The output will be uniquely identified by
A messagehandler method must be provided to the constructor of a ActorOutput. This handler will be called, when an unexpected message is received from service.
WcfDefault.StartAppInstance (args, new WcfTrc.PluginFile(), /*ExitHandler=*/true); ... Output = new ActorOutput(clientName, OnUnexpectedMessageFromService);
A WcfPartner object may be linked to others by using the 'Link...' methods. These methods just prepare the connection. The communication will not start yet.
We link our ActorOutput to an ActorInput over the network. Method LinkOutputToRemoteService needs the hostname where the WcfRouter runs. It also needs the unique servicename of the ActorInput:
Output.LinkOutputToRemoteService (ServiceHostTextBox.Text, "Test2." + m_serviceName);
AsyncWcfLib contains a tracing system (see [Adapt AsyncWcfLib tracing]). It can write program traces into VisualStudio's output console or into files.
When a 'trace' folder exist at the project root, all test applications write their trace files into this folder.
When switching TraceSend to true, all sent messages are written into trace.
Output.TraceSend = true;
To bring the Actor into life, we call the async method RunAsync. It contains an endless loop but is interrupted by await statements.
Before sending the first message, an ActorOutput must be connected by calling TryConnect.
When no connection is present, the ActorOutput is in either of two states:
During connection buildup a WcfPartnerMessage is automatically exchanged between the two ActorPorts. The state is:
When the first response from ActorInput has been received, the ActorOutput is in the state:
You have to call one of the TryConnect methods to start a connection buildup.
It is important to call TryConnect on the same thread as you would like to receive responses later on.
The thread synchronization context is picked up by TryConnect, as long as you not have set the IsMultithreaded property.
EWcfState s = Output.GetConnectionState(); if (s == EWcfState.Disconnected || s == EWcfState.Faulted) { var rsp = await Output.TryConnectAsync(); }
This call to "TryConnect" is asynchronous. The first response message will be awaited. It is a WcfPartnerMessage from the ActorInput or a WcfErrorMessage, when the connection has not been built up.
See [AsyncWcfLib class diagram messages] for messages provided by the library.
In an automatic system you will typically have a timer eventhandler that will periodically check the connecton and try to establish (when disconnected) or reestablish it (when in faultet state).
The Output.SendReceiveAsync() method will asynchronously try to send your message to the connected ActorInput.
As before, the response (possibly a WcfErrorMessage) is returned after await. Exceptions are not thrown, for 'normal' connection problems. Only when AsyncWcfLib detects programming errors e.g. wrong thread synchronization context, an exception is thrown.
var rsp = await Output.SendReceiveAsync( new WcfIdleMessage() ); if (rsp.Message is Test2Rsp) { ... } else { OnUnexpectedMessageFromService(rsp); }
During await the thread may executed some other task continuations (code between two await statements).
But after await you are on the same thread as before.
Therefore you do not have to care about threadsafety (race conditions, locks or deadlocks).
This is true, when your thread has a message queue like Windows.Forms or NitoAsync.ActionThread (see [Threadsafety and SynchronizationContext]). Test1.ClientNoSync has no message queue.
As response, you receive a WcfReqIdent object containing among others the Message and the Sender properties. Sender is the ActorPort object identifying the ActorInput (service).
AsyncWcfLib allows a service to reply several messages when receiving one request.
When a connection is physically interrupted, a WcfErrorMessage will be received after a timeout period.
These are both unexpected messages that are routed to the default message handler:
void OnUnexpectedMessageFromService( WcfReqIdent rsp ) { if (rsp.Message is WcfErrorMessage) { WcfTrc.Error (rsp.CltRcvId, rsp.Message+"\r\n"+(rsp.Message as WcfErrorMessage).StackTrace); } else { WcfTrc.Info (rsp.Message+", from "+rsp.Sender.Name); } }
AsyncWcfLib client implementors are strongly advised to periodically send WcfIdleMessages, when no other message has been sent over the connection.
This easy and reliable method ensures you get a timeout WcfErrorMessage on client side and at the same time give the service a chance to check the client connection.
On application shutdown you should disconnect existing connections. This way the service is timely informed about client state.
Output.Disconnect();
When no configuration block for the client is present, the client uses the default binding from WcfLib.WcfDefault.Instance when connected to network.
The binding must be equal to the services binding.
Library users may plug in their own implementation of 'WcfLib.IWcfDefault' or subclass 'WcfLib.WcfDefault.cs' for this reason.
By providing the optional parameter 'clientConfig' to LinkOutputToRemoteService() it is possible to programmatically overwrite the default binding for each client.
See also [Service- and client identification].
Wiki: Adapt AsyncWcfLib tracing
Wiki: AsyncWcfLib class diagram messages
Wiki: AsyncWcfLib package diagram
Wiki: Asynchronous Responses
Wiki: Home
Wiki: Run AsyncWcfLib.Test1
Wiki: Service- and client identification
Wiki: Threadsafety and SynchronizationContext
Wiki: Understand extension methods
Wiki: Understand lambda expressions