Detect disconnect in WCF - wcf

We're building a WCF server (.NET 4.5). It will only use net.pipe transport.
When a client closes the PIPE connection, the server gets unhandled CommunicationException, and terminates.
Q1. How do I handle the CommunicationException so the server does not terminate and continues serving other clients?
Q2. In the handler, how do I get SessionId of the session that was aborted? I need this to do clean up some session-specific data.
Thanks in advance!
contract
[ServiceContract(CallbackContract = typeof(IContractCallback))]
public interface IContractServer
{
[OperationContract(IsOneWay = true)]
void Connect(bool status);
[OperationContract(IsOneWay = false)]
void Disconnect(IContractServer _channelCallback);
[OperationContract(IsOneWay = true)]
void Play(bool status);
}
service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IContractServer
{
public List<IContractCallback> _channeList = new List<IContractCallback>();
public void Connect(bool status)
{
IContractCallback a = OperationContext.Current.GetCallbackChannel<IContractCallback>();
int call = 0;
foreach (var callBack in _channeList)
{
if (callBack == a)
{
call++;
}
}
if (call == 0)
{
_channeList.Add(a);
a.ConnectCallback(true);
}
else
{
a.ConnectCallback(false);
}
}
public void Disconnect(IContractServer _channelCallback)
{
foreach (var contractCallback in _channeList)
{
if (contractCallback == _channelCallback)
{
_channeList.Remove(contractCallback);
}
}
}
public void Play(bool status)
{
foreach (var contractCallback in _channeList)
{
contractCallback.PlayCallback(status);
}
}
}
client
using System.ComponentModel;
using System.ServiceModel;
using System.Windows;
using Host;
namespace VideoPlayer
{
public partial class MainWindow : Window, IContractCallback
{
private IContractServer Proxy = null;
public MainWindow()
{
InitializeComponent();
InstanceContext context = new InstanceContext(this);
DuplexChannelFactory<IContractServer> factory = new DuplexChannelFactory<IContractServer>(context, new NetNamedPipeBinding(), "net.pipe://localhost");
Proxy = factory.CreateChannel();
Proxy.Connect(true);
}
public void ConnectCallback(bool status)
{
MessageBox.Show(status ? "connected" : "no connected");
}
public void PlayCallback(bool status)
{
if (status)
{
MessageBox.Show("status true");
}
else
{
MessageBox.Show("status false");
}
}
private void ButtonPlay(object sender, RoutedEventArgs e)
{
Proxy.Play(true);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
//хочу отправить сообщение о закрытии
Proxy.Disconnect(Proxy);
}

I faced with this problem before in my duplex services when an event raised from server side the exception occurred if there was no alive channel between client and server so server dropped to Fault state and all next requests won't be responded,
For this reason I came to this conclusion to check the channel and if it was alive then let call back methods to be raised.
In service side the trick would be like ↓
bool IsChannelAlive()
{
Logging logging = new Logging(LogFile);
try
{
if (((ICommunicationObject)_callbackChannel).State == CommunicationState.Opened)
{
logging.Log(LoggingMode.Prompt, "Channeld is still alive, can raise events...");
return true;
}
}
catch (Exception exp)
{
logging.Log(LoggingMode.Error, "IsChannelAlive()=> failed, EXP: {0}", exp);
}
logging.Log(LoggingMode.Warning, "Channeld is not alive so events won't raised...");
return false;
}
and in one of my events I use it like :
void stran_OperationTimedOut(object sender, EventArgs e)
{
if (IsChannelAlive())
_callbackChannel.OnOperationTimedOut();
}
But for a while I use this trick to know closed channel to do something:
public ImportService()
{
//Handle ContextClose to Audit all actions made in session
OperationContext.Current.InstanceContext.Closed += delegate
{
//Here
};
}
which is not reliable.
I am still using that IsAliveChannel() in my services.
Hope this answer resolve your problem or give you the clue.

Related

Call request/reply method of duplex service freeze client until timeout occur

I have create a duplex WCF service tha return data from an external device to the client and allow also request/reply calls.
My problem is that the request/reply calls sometime freeze the client until timeout occurs.
These are the interfaces:
[ServiceContract(CallbackContract = typeof(ITimerServiceCallback), SessionMode = SessionMode.Required)]
public interface ITimerService
{
[OperationContract]
void StartTimer();
[OperationContract]
void StopTimer();
[OperationContract]
void DoOp();
}
public interface ITimerServiceCallback
{
[OperationContract(IsOneWay = true)]
void ReportTick(string now);
}
This is the implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TimerService : ITimerService
{
public TimerService()
{
_timer = new System.Timers.Timer() { Interval = 500 };
_timer.Elapsed += Timer_Elapsed;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
_context.GetCallbackChannel<ITimerServiceCallback>().ReportTick(DateTime.Now.ToString());
}
private readonly System.Timers.Timer _timer;
private OperationContext _context;
public void StartTimer()
{
_context = OperationContext.Current;
_timer.Start();
}
public void StopTimer()
{
_timer.Stop();
}
public void DoOp()
{
System.Threading.Thread.Sleep(200);
}
}
Step to reproduce the problem:
* call StartTimer
* call DoOp (one or more time until the client freeze)
After one minute I have an timeout exception.
If I use the async/await from the client side all work fine.
ex: the DoOp method is called in this way:
private async void _btnDoOp_Click(object sender, EventArgs e)
{
_btnNoOp.Enabled = false;
try {
Task task = Task.Run(() => _client.DoOperation());
await task;
} catch (Exception ex) {
MessageBox.Show(ex.Message);
} finally {
_btnNoOp.Enabled = true;
}
}

Can not apply EAP pattern to WCF Service Client on Windows Phone 8

I am trying to apply EAP pattern to call WCF service. Same code runs smoothly on Windows Console Application but hangs on indefinetely on Task.Wait on Windows Phone application.
Any ideas about the problem will be highly appreciated.
Thanks in advance
Here is the extension method and helper function:
public static class ServiceExtensions
{
public static Task<string> DoGlobalizationResourcesInquiryAsyncTask(
this Service1Client serviceClient,
int request)
{
var tcs = new TaskCompletionSource<string>();
EventHandler<GetDataCompletedEventArgs> handler = null;
handler = (sender, e) => EAPHelpers.TransferCompletion(
tcs,
e,
() => e.Result,
() => serviceClient.GetDataCompleted -= handler);
serviceClient.GetDataCompleted += handler;
try
{
serviceClient.GetDataAsync(request, tcs);
}
catch
{
serviceClient.GetDataCompleted -= handler;
tcs.TrySetCanceled();
throw;
}
return tcs.Task;
}
}
public static class EAPHelpers
{
public static void TransferCompletion<T>(
TaskCompletionSource<T> tcs,
AsyncCompletedEventArgs e,
Func<T> getResult,
Action unregisterHandler)
{
if (e.UserState == tcs)
{
if (e.Cancelled) tcs.TrySetCanceled();
else if (e.Error != null) tcs.TrySetException(e.Error);
else tcs.TrySetResult(getResult());
if (unregisterHandler != null) unregisterHandler();
}
}
}
I call the extension method as follows:
Service1Client client = new Service1Client();
var task = client.DoGlobalizationResourcesInquiryAsyncTask(3);
task.Wait();
var response = task.Result;

wcf callback timeout when sending more than one messages

I have a wcf callback programm and it can send a message to a client.
If i try to send a second message the whole programm freezes and i get a timeoutexception.
Here is the servercode:
public void SendMessageToClient(string computerName, string message)
{
foreach (var session in connectedClients.Values)
{
if (session.ComputerName == computerName)
{
var asyncResult = session.Callback.BeginOnMessageReceived(message, new AsyncCallback(OnPushMessageComplete), session.Callback);
if (asyncResult.CompletedSynchronously)
CompletePushMessage(asyncResult);
}
}
}
void OnPushMessageComplete(IAsyncResult asyncResult)
{
CompletePushMessage(asyncResult);
}
void CompletePushMessage(IAsyncResult asyncResult)
{
var callbackChannel = (IServiceCallback)asyncResult.AsyncState;
try
{
callbackChannel.EndOnMessageReceived(asyncResult);
}
catch { }
}
And this is the Callbackinterface:
[OperationContract(IsOneWay = true, AsyncPattern = true)]
IAsyncResult BeginOnMessageReceived(string message, AsyncCallback acb, object state);
void EndOnMessageReceived(IAsyncResult iar);
And this is the client code:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public class ServiceHandler : IServiceCallback
{
public delegate void MessageReceivedHandler(string message);
public event MessageReceivedHandler OnMessageReceivedEvent;
public void OnMessageReceived(string message)
{
if (this.OnMessageReceivedEvent != null)
this.OnMessageReceivedEvent(message);
}
}
void callback_OnMessageReceivedEvent(string message)
{
setlb_info(message)
}
public void setlb_info(string wert)
{
if (this.lb_info.InvokeRequired)
{
setlb_infoCallback d = new setlb_infoCallback(setlb_info);
this.Invoke(d, new object[] { wert });
}
else
{
this.lb_info.Text = wert;
}
}
And if i try this:
Service.CurrentInstance.SendMessageToClient(client_name, message);
the client will get the message but if i call the same method a second time i get the timeoutexception (which is set to 1 minute).
I'm using code from this project(german):
http://www.flexbit.at/blog/wcf-duplex-zwischen-windows-sevice-und-gui-frontend/
I hope someone can help me because i can't finish my work if this function won't work.
Best regards
EDIT: I forgot a code on the client side:
var callback = new ServiceHandler();
callback.OnMessageReceivedEvent += new ServiceHandler.MessageReceivedHandler(callback_OnMessageReceivedEvent);
var callbackInstanceContext = new InstanceContext(callback);
client = new ServiceClient(callbackInstanceContext);
client.Subscribe(System.Environment.MachineName);
Try change
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
to
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]

WCF duplex TCP communication error

I have a sample service to test WCF net.tcp communication. It is very simple service and all it does is subscribing a client to the service and then calls callbackchannel to notify all connected clients about broadcasted message. The service is hosted inside IIS 7.5.
Here is service code and test client to test it.
[ServiceContract(CallbackContract = typeof(ISampleServiceCallBack), SessionMode = SessionMode.Required)]
public interface ISampleCuratioService
{
[OperationContract(IsOneWay = true)]
void SubcribeToService(string sub);
[OperationContract]
string GetData(int value);
[OperationContract(IsOneWay = true)]
void Broadcast(string message);
}
public interface ISampleServiceCallBack
{
[OperationContract(IsOneWay = true)]
void NotifyClient(string message);
}
Here is the service implementation:
[ServiceBehavior(Name = "CuratioCSMService", InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : ISampleCuratioService
{
private static List<ISampleServiceCallBack> JoinedClien = new List<ISampleServiceCallBack>();
public void SubcribeToService(string sub)
{
var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>();
if (!JoinedClien.Contains(subscriber))
{
JoinedClien.Add(subscriber);
}
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public void Broadcast(string message)
{
JoinedClien.ForEach(c => c.NotifyClient("message was received " + message));
}
}
I can not understand the behavior I get when running it. After the first client runs everything works fine but as I close and open test client app, it throws exception notifying that channel can not be used for communication as it is in fault state.
This is sample test client:
static void Main(string[] args)
{
var callneckclient = new ServiceClientProxy();
var client = new SampleCuratioServiceClient(new InstanceContext(callneckclient));
client.SubcribeToService("me");
Console.ReadLine();
for (int i = 0; i < 15; i++)
{
Console.WriteLine(client.GetData(5));
client.Broadcast("this is from client me");
}
client.Close();
Console.Read();
}
public class ServiceClientProxy : ISampleCuratioServiceCallback, IDisposable
{
public void NotifyClient(string message)
{
Console.WriteLine(message);
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
The situation gets even buggy when I run 5 clients. Non of those send or receive messages.
When a client calls SubcribeToService you add its operation context to a List called JoinedClien.
When you call Broadcast in your server, you call the method NotifyClient on all collected operation contexts for every client that has ever connected.
The problem is, that a disconnected client won't get removed from your JoinedClien list.
When you try to call an operation method on a disconnected operation context, you get the channel is in faulted state error.
To work around, you should subscribe to the Channel_Closed and Channel_Faulted events and also catch the CommunicationException when calling back into your clients and remove the operation context of the faulted clients:
public void Broadcast(string message)
{
// copy list of clients
List<OperationContext> clientsCopy = new List<OperationContext>();
lock(JoinedClien) {
clientsCopy.AddRange(JoinedClien);
}
// send message and collect faulted clients in separate list
List<OperationContext> clientsToRemove = new List<OperationContext>();
foreach (var c in JoinedClien)
{
try {
c.NotifyClient("message was received " + message));
}
catch (CommunicationException ex) {
clientsToRemove.Add(c);
}
}
foreach (var c in clientsToRemove)
{
lock(JoinedClien) {
if(JoinedClien.Contains(c))
JoinedClien.Remove(c);
}
}
}
When adding new clients you have to lock that operation, too:
var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>();
lock(JoinedClien)
{
if (!JoinedClien.Contains(subscriber))
{
JoinedClien.Add(subscriber);
}
}

How to publish and subscribe to Message class messages using WCF

I have a small WCF pub/sup service running, and remote clients subscribe and send messages (tried will all sorts of complex objects) and that works fine. All interfaces reflect(ed) the type of object being used. Switching to another object type requires that the interfaces be adjusted to accommodate that object type. All subscribers get a copy of the message.
Now I am trying to do the same thing, but with Message class messages. Client creates a new message and encapsulates its object in the message, and sends it to the (remote) service, where it is received properly (inspected the object). However, when the server replies by resending (callback) the message back to the originating client, the client receives the following message:
“The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.”
Sequence of events (Client):
Client creates Message,
(DuplexChannelFactory)AddMessage,
-Catch above error
Sequence of events (Server):
Service host receives message,
Message inspected (copy and recreate),
Perform callback,
No errors.
Switching back to a basic, or user defined, type and all problems go away. I have been struggling with this for a week now and not closer to any solution. Tried manipulating headers, recreating the message, switching to Message Contracts, and trying to interpret the contents of the trace logs etc. Hope I will find some answers here.
Primary code used (stripped of most of the error handling):
Client interfaces:
namespace WCFSQL
{
public class ClientInterfaces
{
[ServiceContract(Namespace = "WCFServer", Name = "CallBacks")]
public interface IMessageCallback
{
[OperationContract(Name = "OnMessageAdded", Action = "WCFServer/IMessageCallback/OnMessageAdded", IsOneWay = true)]
void OnMessageAdded(Message SQLMessage, DateTime timestamp);
}
[ServiceContract(Namespace = "WCFServer", CallbackContract = typeof(IMessageCallback))]
public interface IMessage
{
[OperationContract(Name = "AddMessage", Action = "WCFServer/IMessage/AddMessage")]
void AddMessage(Message SQLMessage);
[OperationContract(Name = "Subscribe", Action = "WCFServer/IMessage/Subscribe")]
bool Subscribe();
[OperationContract(Name = "Unsubscribe", Action = "WCFServer/IMessage/Unsubscribe")]
bool Unsubscribe();
}
}
}
Server interfaces:
namespace WCFSQL
{
public class ServerInterfaces
{
[ServiceContract(Namespace = "WCFServer")]
public interface IMessageCallback
{
[OperationContract(Name = "OnMessageAdded", Action = "WCFServer/IMessageCallback/OnMessageAdded", IsOneWay = true)]
void OnMessageAdded(Message SQLMessage, DateTime timestamp);
}
[ServiceContract(Namespace = "WCFServer", CallbackContract = typeof(IMessageCallback), SessionMode = SessionMode.Required)]
public interface IMessage
{
[OperationContract(Name = "AddMessage", Action = "WCFServer/IMessage/AddMessage")]
void AddMessage(Message SQLMessage);
[OperationContract(Name = "Subscribe", Action = "WCFServer/IMessage/Subscribe")]
bool Subscribe();
[OperationContract(Name = "Unsubscribe", Action = "WCFServer/IMessage/Unsubscribe")]
bool Unsubscribe();
}
}
}
Message creation:
// client proxy instance created and opened before
public static bool WCFSqlLogger(string Program, WCFSQLErrorLogMessage SQLErrorMessage, WCFSqlClientProxy client)
{
MessageVersion ver = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10);
Message Out = Message.CreateMessage(ver, "WCFServer/IMessage/AddMessage", SQLErrorMessage);
if (!client.SendMessage(Out))
{
Console.WriteLine("Client Main: Unable to send");
return false;
}
return true;
}
Client proxy:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
[CallbackBehavior(IncludeExceptionDetailInFaults = true, ConcurrencyMode = ConcurrencyMode.Single, UseSynchronizationContext = false)]
public class WCFSqlClientProxy : ClientInterfaces.IMessageCallback, IDisposable
{
public ClientInterfaces.IMessage pipeProxy = null;
DuplexChannelFactory<ClientInterfaces.IMessage> pipeFactory;
public bool Connect()
{
NetTcpBinding newBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);// NetTcpBinding newBinding = new NetTcpBinding(SecurityMode.Transport)
newBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
EndpointAddress newEndpoint = new EndpointAddress(new Uri("net.tcp://host:8000/ISubscribe"), EndpointIdentity.CreateDnsIdentity("Domain"));
pipeFactory = new DuplexChannelFactory<ClientInterfaces.IMessage>(new InstanceContext(this), newBinding, newEndpoint);
pipeFactory.Credentials.Peer.PeerAuthentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
pipeFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
pipeFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.TrustedPeople, X509FindType.FindByThumbprint, "somestring");
try
{
pipeProxy = pipeFactory.CreateChannel();
pipeProxy.Subscribe();
return true;
}
catch (Exception e)
{
Console.WriteLine("Error opening: {0}", e.Message);
return false;
}
}
public void Close()
{
pipeProxy.Unsubscribe();
}
public bool SendMessage(Message SQLMessage)
{
try
{
Console.WriteLine("Proxy Sending:");
pipeProxy.AddMessage(SQLMessage); // This is where the eror occurs !!!!!!!!!!!!!!!!!!
return true;
}
catch (Exception e)
{
Console.WriteLine("Client Proxy: Error sending: {0}", e.Message);
}
return false;
}
public void OnMessageAdded(Message SQLMessage, DateTime timestamp)
{
WCFSQLErrorLogMessage message = SQLMessage.GetBody<WCFSQLErrorLogMessage>();
Console.WriteLine(message.LogProgram + ": " + timestamp.ToString("hh:mm:ss"));
}
public void Dispose()
{
Console.WriteLine("Dispose: Unsubscribe");
pipeProxy.Unsubscribe();
}
}
Service:
namespace WCFSQL
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
[CallbackBehavior(IncludeExceptionDetailInFaults = true, ConcurrencyMode = ConcurrencyMode.Single, UseSynchronizationContext = false)] // or ConcurrencyMode.Reentrant
public class WCFSqlServerProxy : ServerInterfaces.IMessage
{
private static List<ServerInterfaces.IMessageCallback> subscribers = new List<ServerInterfaces.IMessageCallback>();
private static Uri target;
private static ServiceHost serviceHost;
public WCFSqlServerProxy(Uri Target) // Singleton
{
target = Target;
}
public bool Connect()
{
serviceHost = new ServiceHost(typeof(WCFSqlServerProxy), target);
NetTcpBinding newBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
newBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
newBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
serviceHost.Credentials.ClientCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck; // Non-domain members cannot follow the chain?
serviceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.TrustedPeople, X509FindType.FindByThumbprint, "somestring");
serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
serviceHost.AddServiceEndpoint(typeof(ServerInterfaces.IMessage), newBinding, "ISubscribe");
return true;
}
public bool Open()
{
serviceHost.Open();
return true;
}
public bool Close()
{
serviceHost.Close();
return true;
}
public bool Subscribe()
{
try
{
ServerInterfaces.IMessageCallback callback = OperationContext.Current.GetCallbackChannel<ServerInterfaces.IMessageCallback>();
if (!subscribers.Contains(callback))
{
subscribers.Add(callback);
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
return false;
}
}
public bool Unsubscribe()
{
try
{
ServerInterfaces.IMessageCallback callback = OperationContext.Current.GetCallbackChannel<ServerInterfaces.IMessageCallback>();
if (subscribers.Contains(callback))
{
subscribers.Remove(callback);
return true;
}
return false;
}
catch (Exception e)
{
Console.WriteLine("WCFSqlServerProxy: Unsubscribe - Unsubscribe error {0}", e);
return false;
}
}
private string GetData()
{
MessageProperties messageProperties = ((OperationContext)OperationContext.Current).IncomingMessageProperties;
RemoteEndpointMessageProperty endpointProperty = messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
string computerName = null;
try
{
string[] computer_name = Dns.GetHostEntry(endpointProperty.Address).HostName.Split(new Char[] { '.' });
computerName = computer_name[0].ToString();
}
catch (Exception e)
{
computerName = "NOTFOUND";
Console.WriteLine("WCFSqlServerProxy: Hostname error: {0}", e);
}
return string.Format("{0} - {1}:{2}", computerName, endpointProperty.Address, endpointProperty.Port);
}
public void AddMessage(Message SQLMessage) //Go through the list of connections and call their callback funciton
{
subscribers.ForEach(delegate(ServerInterfaces.IMessageCallback callback)
{
if (((ICommunicationObject)callback).State == CommunicationState.Opened)
{
MessageVersion ver = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10);
MessageBuffer buffer = SQLMessage.CreateBufferedCopy(4096);
Message msgCopy = buffer.CreateMessage();
//System.Xml.XmlDictionaryReader xrdr = msgCopy.GetReaderAtBodyContents();
WCFSQLErrorLogMessage p = msgCopy.GetBody<WCFSQLErrorLogMessage>();
SQLMessage = buffer.CreateMessage();
buffer.Close();
Message In = Message.CreateMessage(ver, "WCFServer/IMessage/AddMessage", p); // Tried recreating messsage, with same results
//Console.WriteLine("Message: Header To: {0}", In.Headers.To);
//Console.WriteLine("Message: Header From: {0}", In.Headers.From);
//Console.WriteLine("Message: Header Action: {0}", In.Headers.Action);
//Console.WriteLine("Message: Header ReplyTo: {0}", In.Headers.ReplyTo);
//Console.WriteLine("Message: IsFault: {0}", In.IsFault);
//Console.WriteLine("Message: Properties {0}", In.Properties);
//Console.WriteLine("Message: State {0}", In.State);
//Console.WriteLine("Message: Type {0}", In.GetType());
//Console.WriteLine("Proxy Sending: Copy created");
//Console.WriteLine("Remote: {0}, Hash: {1}", GetData(), callback.GetHashCode());
callback.OnMessageAdded(SQLMessage, DateTime.Now); // This should echo the message back with a timeslot.
}
else
{
Console.WriteLine("WCFSqlServerProxy:addmessage connected state: {0}", ((ICommunicationObject)callback).State == CommunicationState.Opened);
subscribers.Remove(callback);
}
});
}
}
I just got an answer to my question from Tanvir Huda, on the microsoft WCF forum.
"Using the Message Class in Operations
You can use the Message class as an input parameter of an operation, the return value of an operation, or both. If Message is used anywhere in an operation, the following restrictions apply:
•The operation cannot have anyoutorrefparameters.
•There cannot be more than oneinputparameter. If the parameter is present, it must be either Message or a message contract type.
•The return type must be either void,Message, or a message contract type."
I cannot beleive I missed that; must have read it at least three times, but never applied those rules to the callback. The callback in my interfaces described, do have a return type of void, but it has a Message and a DateTime parameter.
After removing the DateTime parameter, the callback did (try) to re-serialize the original Message, but failed because of on invalid action (action was still set for the AddMessage, while now it should be OnMessageAdded). After changing the action on the callback to Action="*" it workt perfectly. A (maybe annoying) detail it that i do not really require a Message type on the callback, but I was very frustrated that I did not get it to work