I have a Console application hosting a WCF service. I would like to be able to fire an event from a method in the WCF service and handle the event in the hosting process of the WCF service. Is this possible? How would I do this? Could I derive a custom class from ServiceHost?
You don't need to inherit from ServiceHost. There are other approaches to your problem.
You can pass an instance of the service class, instead of a type to ServiceHost. Thus, you can create the instance before you start the ServiceHost, and add your own event handlers to any events it exposes.
Here's some sample code:
MyService svc = new MyService();
svc.SomeEvent += new MyEventDelegate(this.OnSomeEvent);
ServiceHost host = new ServiceHost(svc);
host.Open();
There are some caveats when using this approach, as described in http://msdn.microsoft.com/en-us/library/ms585487.aspx
Or you could have a well-known singleton class, that your service instances know about and explicitly call its methods when events happen.
using ...
using ...
namespace MyWCFNamespace
{
class Program {
static void Main(string[] args){
//instantiate the event receiver
Consumer c = new Consumer();
// instantiate the event source
WCFService svc = new WCFService();
svc.WCFEvent += new SomeEventHandler(c.ProcessTheRaisedEvent);
using(ServiceHost host = new ServiceHost(svc))
{
host.Open();
Console.Readline();
}
}
}
public class Consumer()
{
public void ProcessTheRaisedEvent(object sender, MyEventArgs e)
{
Console.WriteLine(e.From.toString() + "\t" + e.To.ToString());
}
}
}
namespace MyWCFNamespace
{
public delegate void SomeEventHandler(object sender,MyEventArgs e)
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class WCFService : IWCFService
{
public event SomeEventHandler WCFEvent;
public void someMethod(Message message)
{
MyEventArgs e = new MyEventArgs(message);
OnWCFEvent(e);
}
public void OnWCFEvent(MyEventArgs e)
{
SomeEventHandler handler = WCFEvent;
if(handler!=null)
{
handler(this,e);
}
}
// to do
// Implement WCFInterface methods here
}
public class MyEventArgs:EventArgs
{
private Message _message;
public MyEventArgs(Message message)
{
this._message=message;
}
}
public class Message
{
string _from;
string _to;
public string From {get{return _from;} set {_from=value;}}
public string To {get{return _to;} set {_to=value;}}
public Message(){}
public Message(string from,string to)
this._from=from;
this._to=to;
}
}
You can define your WCF service with InstanceContextMode = InstanceContextMode.Single.
TestService svc = new TestService();
svc.SomeEvent += new MyEventHandler(receivingObject.OnSomeEvent);
ServiceHost host = new ServiceHost(svc);
host.Open();
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] // so that a single service instance is created
public class TestService : ITestService
{
public event MyEventHandler SomeEvent;
...
...
}
Related
I will keep it simple. I have two services in my ASP.NET Core WebAPI project.
ServiceA and ServiceB.
Service A is responsible for sending emails when Service B raises an event. Service B has an event handler and a delegate and the event is raised correctly. However, the problem is in Service A when trying to handle the event via += EventHandlingMethod();
The handler method EventHandlingMethod() is never called. I have placed a breakpoint inside the method but it never triggers, event after method EventRaised() has been called from Service B correctly.
Is it possible because Service A is set up in Startup.cs as services.AddTransient<IServiceA, ServiceA>();?
I have provided a simple example of publish-subscribe pattern. This is synchronous but if you are looking for an asynchronous version you can use channels or any other message broker such as RabbitMQ / NServiceBus etc.
public class PublishSubscribeMiddleMan: IPubSub
{
Dictionary<Type, List<ISubscriber>> pubSub = new Dictionary<Type, List<ISubscriber>>();
public void PublishEvent<Publisher>(Publisher publisher)
{
Type t = publisher.GetType();
if (pubSub.TryGetValue(t, out var subscribers))
{
subscribers.ForEach(subscriber => subscriber.EventHandlingMethod());
}
}
public void Subscribe<Publisher>(ISubscriber subscriber)
{
Type t = typeof(Publisher);
if (pubSub.TryGetValue(t, out var subscribers))
{
subscribers.Add(subscriber);
}
else pubSub.Add(t, new List<ISubscriber> { subscriber });
}
}
public interface ISubscriber
{
void EventHandlingMethod();
}
public interface IPubSub
{
void Subscribe<Publisher>(ISubscriber subscriber);
void PublishEvent<Publisher>(Publisher publisher);
}
public class ServiceA : IServiceA
{
private readonly IPubSub publishSubscribe;
public ServiceA(IPubSub publishSubscribe)
{
this.publishSubscribe = publishSubscribe;
}
public void RaiseEvent()
{
publishSubscribe.PublishEvent(this);
}
}
public interface IServiceA
{
void RaiseEvent();
}
public class ServiceB : ISubscriber
{
public ServiceB(IPubSub publishSubscribe)
{
publishSubscribe.Subscribe<ServiceA>(this);
}
public void EventHandlingMethod()
{
//throw new NotImplementedException();
}
}
You would need to register the PubSub inside ConfigureServices as shown:
services.AddScoped<IPubSub, PublishSubscribeMiddleMan>();
services.AddTransient<IServiceA, ServiceA>();
I'm followed this example to create a WCF interface between two C# projects:
https://dopeydev.com/wcf-interprocess-communication/
as you can see in the "TestService.cs" code, there is an implementation of this function:
public void SendMessage(string message)
{
MessageBox.Show(message);
}
which actually shows the received message on the server. I know the name is quite confusing, but it's the same name the client would use.
My goal is to change this function in order to fire an event to be consumed in another class.
This is what I tried to do:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class IServer : Interfaces.IService
{
public void Connect()
{
Callback = OperationContext.Current.GetCallbackChannel<Interfaces.ICallbackService>();
}
public static Interfaces.ICallbackService Callback { get; set; }
public void SendMessage(string message)
{
MessageReceivedEventArgs args = new MessageReceivedEventArgs();
args.json = message;
OnMessageReceived(this, args);
}
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
protected virtual void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
MessageReceived?.Invoke(this, e);
}
}
public class MessageReceivedEventArgs : EventArgs
{
public string json;
}
then in my other class:
class Comm
{
IServer m_server = new IServer();
public Engine()
{
var host = new ServiceHost(typeof(IServer), new Uri("net.pipe://localhost"));
host.AddServiceEndpoint(typeof(IService), new NetNamedPipeBinding(), "ipc");
host.Open();
m_server.MessageReceived += Server_MessageReceived;
}
private void Server_MessageReceived(object sender, MessageReceivedEventArgs e)
{
// this event handler is never executed!
}
}
As far as I understand, the problem is I create a new instance of the IServer class with the event handler added, but the ServiceHost which actually uses the IServer code is another instance.
Am I wrong?
I need to retrieve the instance used by ServiceHost to get the event working?
ServiceHost has an overload where you can provide your own instance for the service:
var host = new ServiceHost(m_server, new Uri("net.pipe://localhost"));
Also, you should bind the event before starting the service, otherwise you could miss requests:
m_server.MessageReceived += Server_MessageReceived;
host.Open();
I have two processes and the second process needs to have access to a singleton in the first process. So I wrote a server that should help sharing the instance.
Something is wrong though, it seems like the client gets its own version of the singleton rather than the original instance.
The minimal example comes in two projects. Here is the client:
Program
using IPCServer;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
namespace IPCEventTest
{
class IPCClient
{
static void Main(string[] args)
{
Process ipcserver = Process.Start("IPCServer.exe");
Thread.Sleep(2000);
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Connect");
Module module = Connect("WellKnownName"); //this name is used by the host
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Connect");
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Raise");
module.RaiseEvent(); //raise event should raise within the server process
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Raise");
while (true) ;
}
public static Module Connect(string id)
{
Console.WriteLine("Start Connect");
ChannelFactory<IModuleServer> pipeFactory =
new ChannelFactory<IModuleServer>(
new NetNamedPipeBinding(),
new EndpointAddress($"net.pipe://localhost/{id}")
);
IModuleServer serverProxy = pipeFactory.CreateChannel();
Module ret = serverProxy.GetModule();
Console.WriteLine("End Connect");
return ret;
}
}
}
The following files set up the Host:
Program
using System;
namespace IPCServer
{
class Program
{
static HOST host;
static void Main(string[] args)
{
host = new HOST("WellKnownName");
Module.Instance.myevent += Instance_myevent;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Subscribed to {Module.Instance.id}");
while (true) ;
}
private static void Instance_myevent(object sender, EventArgs e)
{
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Event Fired from {(sender as Module).id}");
}
}
}
Module
using System;
using System.Linq;
namespace IPCServer
{
public class Module
{
public static Module Instance { get; } = new Module();
public event EventHandler myevent = delegate { };
public string id;
private Module()
{
var guid4 = Guid.NewGuid().ToString().Take(4);
id = new String(guid4.ToArray());
Console.WriteLine($"Module Constructor {id}");
myevent += Module_myevent;
}
private void Module_myevent(object sender, EventArgs e)
{
Console.WriteLine($"Module Listener {(sender as Module).id}");
}
public void RaiseEvent()
{
Console.WriteLine($"Module Start Raise {id}");
myevent(this, EventArgs.Empty);
Console.WriteLine($"Module End Raise {id}");
}
}
}
Host
using System;
using System.ServiceModel;
namespace IPCServer
{
internal class HOST
{
ServiceHost host;
internal HOST(string id)
{
host = new ServiceHost(typeof(ModuleServer), new Uri[] { new Uri("net.pipe://localhost") });
host.AddServiceEndpoint(typeof(IModuleServer), new NetNamedPipeBinding(), id);
host.Open();
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Opened");
}
~HOST()
{
if (host.State == CommunicationState.Opened)
{
host.Close();
}
host = null;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Destructed");
}
}
[ServiceContract]
public interface IModuleServer
{
[OperationContract]
Module GetModule();
}
public class ModuleServer : IModuleServer
{
public Module GetModule()
{
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer start GetModule");
Module ret = Module.Instance;
Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer end GetModule");
return ret;
}
}
}
The example runs and here is the output on my system:
Why am i not getting my Singleton from the server process.
Why is my event not raised in the server.
Edit: The Server opens the host and also subscribes to the singleton. After the client connects it raises the event via member function. Event has two subscribers, one in the constructor and one on the server side. only the Module internal subscription is handled - there is no eventhandling on the server side - no event is fired on the server side. The module listener is triggered but not inside the host process. This event is handled on the client side.
Why am i not getting my Singleton from the server process
Named pipes use Serialization to pass the object from server to client. That means that the client side have to re-run the constructor and copy existing properties.
public string id; is a field so it won't be 'copied' so the random value set by the constructor is left. That's why you have different Ids for the "same" object.
To solve this, you can change this to :
[DataContract]
public class Module
{
[DataMember]
public string id {get; set;}
}
Why is my event not raised in the server.
This is not how WCF named pipes works as you just have a duplicate version a client side. I suggest you to read about Duplex Channel
I am trying to listen to event which is raised from application running under a windows service. Below is sample code to demonstrate as what I am after ...
Separate Class library code(classlibrary1.dll)
namespace ClassLibrary1
{
public class Class1 : MarshalByRefObject
{
public event TestServiceDel TestEvent;
TestEventArgs args1 = new TestEventArgs
{
Data = "Hello Buddy ... How Is Life!!!!!"
};
public void RaiseEvent()
{
if (!EventLog.SourceExists("TestService"))
EventLog.CreateEventSource("TestService", "Application");
EventLog.WriteEntry("TestService","Before raising event ...");
if (TestEvent != null)
TestEvent(this, args1);
EventLog.WriteEntry("TestService","After raising event ...");
}
}
Hosted Windows Service (windowsservice1exe)
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
System.Threading.Thread.Sleep(1000);
ClassLibrary1.Class1 cs = new ClassLibrary1.Class1();
cs.RaiseEvent();
}
Client application (consoleapp1.exe)
class Program : MarshalByRefObject
{
static void Main(string[] args)
{
Class1 cs = new Class1();
cs.TestEvent += Cs_TestEvent;
Console.ReadLine();
}
private static void Cs_TestEvent(object sender, TestEventArgs args)
{
EventLog.WriteEntry("TestService",args.Data, EventLogEntryType.Information);
Console.WriteLine(args.Data);
}
As you can see, I have tried subclassing MarshalByRefObject for both publisher/subscriber but it's not working. I mean I can see in event log that the events are getting raised but my client application never receives the notification (or) the notification never reaches.
Please let me know how to achieve this.
Since my service and client both running in same machine, I believe I don't need to use Remoting. Right?
Apologies had a typo...have edited...
I have a weird issue I am not sure about.
In one piece of code I have a class which is called as a singleton which has an event other classes can listen to, pretty straightforward by doing something like
Client.Instance.MyEvent += new EventHandler<EventArgs>(myHandler);
So if I have a generic class:
Class MyTest {
public MyTest() {
System.Console.WriteLine("In Constructor Registering Events");
Client.Instance.MyEvent += new EventHandler<EventArgs>(myHandler);
}
private void myHandler(object sender, EventArgs arg) {
System.Console.WriteLine("Got event!");
}
}
Now if i create the class like:
MyTest mC = new MyTest ();
Client.Instance.FireEvent();
I get the expected "In Constructor Registering Events" and "Got Event"
However if i create the class through Reflection, I do not.
Type mType = typeof(MyTest);
object mT = Activator.CreateInstance(mType);
Client.Instance.FireEvent();
All i get is "In Constructor Registering Events" but i DO NOT get the event fired message. whats going on here? Am i doing something incorrectly in my reflection calls?
Thanks -
I've just tested your claim and, with the proper type, it works the same whether the object is created using new or via reflection.
The following Working Demo can be tested here
public class Program
{
public static void Main(string[] args)
{
Client.Instance.MyEvent += delegate { Console.WriteLine("MY EVENT handled from Main"); };
MyTest mt = new MyTest();
Type mType = typeof(MyTest);
object reflectedMT = Activator.CreateInstance(mType);
Client.Instance.FireEvent();
}
}
public class Client {
private Client() {}
private static Client _inst = new Client();
public static Client Instance { get { return _inst; } }
public void FireEvent() { if(MyEvent != null) MyEvent(this, EventArgs.Empty); }
public event EventHandler<EventArgs> MyEvent;
}
public class MyTest {
public MyTest() {
System.Console.WriteLine("In Constructor Registering Events");
Client.Instance.MyEvent += new EventHandler<EventArgs>(myHandler);
}
private void myHandler(object sender, EventArgs arg) {
System.Console.WriteLine("Got event!");
}
}