I am currently writing a .Net application in c#, which has two main components:
Those two components are currently two seperate projects in my solution. Furthermore, I am using the PRISM 4.0 framework in order to make modules out of those components.
Essentially, the DataGenerator generates a lot of data and sends out events using the EventAggregator from PRISM and the Viewer subscribes to these events and shows the data ready for the user.
Now my requirements have slightly changed and the two components will now run in their own application (but on the same computer). I would still like to have all the communication event-driven and I would also still like to use the PRISM framework.
My first thought was to use WCF for the communication between those two applications. However, there is one thing that makes life a bit harder:
Basically the data that all those events carry are very simple strings, integer and booleans. Could there be a more lightweight way of doing this without WCF?
Finally, it would be nice if the DataGenerator could send out these events and potentially more than one application subscribes to them (or none).
Any suggestions and hints are highly appreciated.
Thanks! Christian
EDIT 1
I am now creating two simple Console applications (one hosting the service and sending the messages, another one receiving the messages) using WCF and Callbacks (as has been suggested). I will add working code as soon as I get this working.
EDIT 2
Okay - Managed to get a simple program running! :) Thanks for your help, guys! Here is the code and a picture of where which classes are:
Let's start with the sender:
In my application, the sender contains the service interfaces and their implementations.
IMessageCallback is the callback interface:
namespace WCFSender
{
interface IMessageCallback
{
[OperationContract(IsOneWay = true)]
void OnMessageAdded(string message, DateTime timestamp);
}
}
ISimpleService is the service contract:
namespace WCFSender
{
[ServiceContract(CallbackContract = typeof(IMessageCallback))]
public interface ISimpleService
{
[OperationContract]
void SendMessage(string message);
[OperationContract]
bool Subscribe();
[OperationContract]
bool Unsubscribe();
}
}
SimpleService is the implementation of ISimpleService:
public class SimpleService : ISimpleService
{
private static readonly List<IMessageCallback> subscribers = new List<IMessageCallback>();
public void SendMessage(string message)
{
subscribers.ForEach(delegate(IMessageCallback callback)
{
if (((ICommunicationObject)callback).State == CommunicationState.Opened)
{
callback.OnMessageAdded(message, DateTime.Now);
}
else
{
subscribers.Remove(callback);
}
});
}
public bool Subscribe()
{
try
{
IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>();
if (!subscribers.Contains(callback))
subscribers.Add(callback);
return true;
}
catch
{
return false;
}
}
public bool Unsubscribe()
{
try
{
IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>();
if (!subscribers.Contains(callback))
subscribers.Remove(callback);
return true;
}
catch
{
return false;
}
}
}
In Program.cs (on the sender side), the Service is hosted and the messages are being send:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class Program : SimpleServiceReference.ISimpleServiceCallback, IDisposable
{
private SimpleServiceClient client;
static void Main(string[] args)
{
ServiceHost myService = new ServiceHost(typeof(SimpleService));
myService.Open();
Program p = new Program();
p.start();
Console.ReadLine();
}
public void start()
{
InstanceContext context = new InstanceContext(this);
client = new SimpleServiceReference.SimpleServiceClient(context, "WSDualHttpBinding_ISimpleService");
for (int i = 0; i < 100; i++)
{
client.SendMessage("message " + i);
Console.WriteLine("sending message" + i);
Thread.Sleep(600);
}
}
public void OnMessageAdded(string message, DateTime timestamp)
{
throw new NotImplementedException();
}
public void Dispose()
{
client.Close();
}
}
Furthermore, note that the service reference has been added to the Sender project!
Lets now get to the Receiver side:
As has already been done in the Sender, I added the Service Reference to the project.
There is only one class, Program.cs:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class Program : SimpleServiceReference.ISimpleServiceCallback, IDisposable
{
private SimpleServiceClient client;
static void Main(string[] args)
{
Program p = new Program();
p.start();
Console.ReadLine();
p.Dispose();
}
public void start()
{
InstanceContext context = new InstanceContext(this);
client = new SimpleServiceReference.SimpleServiceClient(context, "WSDualHttpBinding_ISimpleService");
client.Subscribe();
}
public void OnMessageAdded(string message, DateTime timestamp)
{
Console.WriteLine(message + " " + timestamp.ToString());
}
public void Dispose()
{
client.Unsubscribe();
client.Close();
}
}
The last thing remaining are the app.config files. On the client side, the app.config is automatically generated by adding the service reference. On the server side, I have slightly changed the config, however parts of it are also auto-generated by adding the service reference. Note that you need to do the changes before adding the service reference:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_ISimpleService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" />
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8732/Design_Time_Addresses/WCFSender/SimpleService/"
binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_ISimpleService"
contract="SimpleServiceReference.ISimpleService" name="WSDualHttpBinding_ISimpleService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
<behaviors>
<serviceBehaviors>
<behavior name="MessageBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WCFSender.SimpleService" behaviorConfiguration="MessageBehavior">
<endpoint address="" binding="wsDualHttpBinding" contract="WCFSender.ISimpleService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/Design_Time_Addresses/WCFSender/SimpleService/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
IMPORTANT: I managed to implement these two very simple applications using the tutorials. The above code works for me and hopefully helps others to understand WCF callback. It is not extremely well-written code and should not be used entirely! It is just a simplistic example app.
Dont worry about performance, wcf can reach very high throughput if configured properly. Use callbacks for your events : http://www.switchonthecode.com/tutorials/wcf-tutorial-events-and-callbacks