Environment: .net 4.6.1 on Windows 10 x64
Problem:
1. WCF Contract implemented with an event.
2. Service host will fire the event every time an event log entry is logged and called by the client.
3. The host application triggers properly however the object (EventLogEntry) does not get posted properly and is always null.
4. I have checked during debug the client side Winforms application passes a instantiated object. This somehow doesn't make it to the
server side.
5. at this Stage both the client and the server are on the same machine.
Question:
Why is object null and how can I fix this?
WCF Service Code:
[ServiceContract]
public interface IloggerWCFServiceContract
{
[OperationContract(IsOneWay = true)]
void WriteEvent(EntryWrittenEventArgs e);
[OperationContract(IsOneWay = true)]
[ServiceKnownType(typeof(EventLogEntry))]
void OnEventWritten(EntryWrittenEventArgs e);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class wcf_logger_servicebase : IloggerWCFServiceContract
{
public event EventHandler<EntryWrittenEventArgs> eventwritten;
public void OnEventWritten(EntryWrittenEventArgs e)
{
if (eventwritten != null)
this.eventwritten(this, e);
}
public void WriteEvent(EntryWrittenEventArgs e)
{
OnEventWritten(new EntryWrittenEventArgs(e.Entry));
}
}
Wcf Winforms Server code:
wcf_logger_servicebase loggerWCFService = new wcf_logger_servicebase();
ServiceHost loggerServiceHost = new ServiceHost(loggerWCFService, new Uri(loggerGlobalStatics.namedPipeServerAddress));
loggerWCFService.eventwritten += new EventHandler<EntryWrittenEventArgs>(eventhandler_entryWritten);
.....
loggerServiceHost.AddServiceEndpoint(typeof(IloggerWCFServiceContract), new NetNamedPipeBinding(), loggerGlobalStatics.namedPipeServerAddress);
loggerServiceHost.Open();
......
private void eventhandler_entryWritten(object sender, EntryWrittenEventArgs e)
{
--->textBox1.Text += e.Entry.ToString() + "\r\n" ;
}
WCF Winforms Client code:
ChannelFactory<IloggerWCFServiceChannel> loggerChannelFactory;
IloggerWCFServiceChannel loggerServiceChannel = null;
wcf_logger_servicebase loggerWCFService = new wcf_logger_servicebase();
EndpointAddress serverEndpoint;
System.ServiceModel.Channels.Binding binding;
public client()
{
InitializeComponent();
serverEndpoint = new EndpointAddress(new Uri(loggerGlobalStatics.namedPipeServerAddress));
binding = new NetNamedPipeBinding();
loggerChannelFactory = new ChannelFactory<IloggerWCFServiceChannel>(binding, serverEndpoint);
loggerChannelFactory.Open();
loggerServiceChannel = loggerChannelFactory.CreateChannel();
loggerServiceChannel.Open();
....
private void eventLog1_EntryWritten(object sender, System.Diagnostics.EntryWrittenEventArgs e)
{
loggerServiceChannel.WriteEvent(e);
}
The reason is that EntryWrittenEventArgs has no setter for Entry.
See this SO question for more details.
Why not just send Entry directly without encapsulating it in EntryWrittenEventArgs?
Related
Hey all I have the following code that allows me to start a temp web service for WCF services without spooling up IIS Express myself.
However, the code below works just fine if I have the fetching of the data in another winform. Copying the same code over to the WCF service winform results in it freezing when trying to send patient index number.
The class code (Class1.cs):
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
/*
HOW TO HOST THE WCF SERVICE IN THIS LIBRARY IN ANOTHER PROJECT
You will need to do the following things:
1) Add a Host project to your solution
a. Right click on your solution
b. Select Add
c. Select New Project
d. Choose an appropriate Host project type (e.g. Console Application)
2) Add a new source file to your Host project
a. Right click on your Host project
b. Select Add
c. Select New Item
d. Select "Code File"
3) Paste the contents of the "MyServiceHost" class below into the new Code File
4) Add an "Application Configuration File" to your Host project
a. Right click on your Host project
b. Select Add
c. Select New Item
d. Select "Application Configuration File"
5) Paste the contents of the App.Config below that defines your service endoints into the new Config File
6) Add the code that will host, start and stop the service
a. Call MyServiceHost.StartService() to start the service and MyServiceHost.EndService() to end the service
7) Add a Reference to System.ServiceModel.dll
a. Right click on your Host Project
b. Select "Add Reference"
c. Select "System.ServiceModel.dll"
8) Add a Reference from your Host project to your Service Library project
a. Right click on your Host Project
b. Select "Add Reference"
c. Select the "Projects" tab
9) Set the Host project as the "StartUp" project for the solution
a. Right click on your Host Project
b. Select "Set as StartUp Project"
################# START MyServiceHost.cs #################
using System;
using System.ServiceModel;
// A WCF service consists of a contract (defined below),
// a class which implements that interface, and configuration
// entries that specify behaviors and endpoints associated with
// that implementation (see <system.serviceModel> in your application
// configuration file).
internal class MyServiceHost
{
internal static ServiceHost myServiceHost = null;
internal static void StartService()
{
//Consider putting the baseAddress in the configuration system
//and getting it here with AppSettings
Uri baseAddress = new Uri("http://localhost:8080/service1");
//Instantiate new ServiceHost
myServiceHost = new ServiceHost(typeof(TestService.service1), baseAddress);
//Open myServiceHost
myServiceHost.Open();
}
internal static void StopService()
{
//Call StopService from your shutdown logic (i.e. dispose method)
if (myServiceHost.State != CommunicationState.Closed)
myServiceHost.Close();
}
}
################# END MyServiceHost.cs #################
################# START App.config or Web.config #################
<system.serviceModel>
<services>
<service name="TestService.service1">
<endpoint contract="TestService.IService1" binding="wsHttpBinding"/>
</service>
</services>
</system.serviceModel>
################# END App.config or Web.config #################
*/
namespace TestService
{
// You have created a class library to define and implement your WCF service.
// You will need to add a reference to this library from another project and add
// the code to that project to host the service as described below. Another way
// to create and host a WCF service is by using the Add New Item, WCF Service
// template within an existing project such as a Console Application or a Windows
// Application.
[ServiceContract()]
public interface IService1
{
[OperationContract]
Patient GetPatient(Int32 index);
[OperationContract]
void SetPatient(Int32 index, Patient patient);
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class PatientService : IService1
{
Patient[] pat = null;
public PatientService()
{
pat = new Patient[3];
pat[0] = new Patient();
pat[0].FirstName = "Bob";
pat[0].LastName = "Chandler";
pat[1] = new Patient();
pat[1].FirstName = "Joe";
pat[1].LastName = "Klink";
pat[2] = new Patient();
pat[2].FirstName = "Sally";
pat[2].LastName = "Wilson";
}
public Patient GetPatient(Int32 index)
{
if (index <= pat.GetUpperBound(0) && index > -1)
return pat[index];
else
return new Patient();
}
public void SetPatient(Int32 index, Patient patient)
{
if (index <= pat.GetUpperBound(0) && index > -1)
pat[index] = patient;
}
}
[DataContract]
public class Patient
{
string firstName;
string lastName;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
}
the Service Code (Form1.cs):
using System;
using System.Windows.Forms;
using System.ServiceModel;
namespace TestService
{
public partial class Form1 : Form
{
bool serviceStarted = false;
ServiceHost myServiceHost = null;
NetTcpBinding binding;
Uri baseAddress = new Uri("net.tcp://localhost:2202/PatientService");
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (serviceStarted)
{
myServiceHost.Close();
serviceStarted = false;
button1.Text = "Start Service";
}
else
{
binding = new NetTcpBinding();
myServiceHost = new ServiceHost(typeof(PatientService), baseAddress);
myServiceHost.AddServiceEndpoint(typeof(IService1), binding, baseAddress);
myServiceHost.Open();
serviceStarted = true;
button1.Text = "Stop Service";
}
}
private void button3_Click(object sender, EventArgs e)
{
IService1 patientSvc = null;
EndpointAddress address = new EndpointAddress(baseAddress);
ChannelFactory<IService1> factory = new ChannelFactory<IService1>(binding, address);
patientSvc = factory.CreateChannel();
Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));
if (patient != null)
{
textBox2.Text = patient.FirstName;
textBox3.Text = patient.LastName;
}
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
The code line here:
Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));
is where it freezes up. Again, this is the same code that is in the other winform and it works. It's just when using it inside the service itself doesn't seem to work the same way for some reason??
The error it gives is this:
System.TimeoutException: 'This request operation sent to net.tcp://localhost:2202/PatientService did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.'
Anyone have an idea on how to get it to work?
This is the other winform code:
using System;
using System.Windows.Forms;
using System.ServiceModel;
using TestService;
namespace TestClient
{
public partial class Form1 : Form
{
IService1 patientSvc = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
EndpointAddress address = new EndpointAddress(new Uri("net.tcp://localhost:2202/PatientService"));
NetTcpBinding binding = new NetTcpBinding();
ChannelFactory<IService1> factory = new ChannelFactory<IService1>(binding, address);
patientSvc = factory.CreateChannel();
}
private void button1_Click(object sender, EventArgs e)
{
Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));
if (patient != null)
{
textBox2.Text = patient.FirstName;
textBox3.Text = patient.LastName;
}
}
private void button2_Click(object sender, EventArgs e)
{
Patient patient = new Patient();
patient.FirstName = textBox2.Text;
patient.LastName = textBox3.Text;
patientSvc.SetPatient(Convert.ToInt32(textBox1.Text), patient);
}
}
}
TestClient code (Program.cs):
using System;
using System.Windows.Forms;
namespace TestClient
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Got it! Thank to #Eser for the hint :)
private void button3_Click(object sender, EventArgs e)
{
IService1 patientSvc = null;
EndpointAddress address = new EndpointAddress(baseAddress);
ChannelFactory<IService1> factory = new ChannelFactory<IService1>(binding, address);
patientSvc = factory.CreateChannel();
Thread thread = new Thread(() => sendData(patientSvc));
thread.Start();
}
delegate void SetTextCallback(string text, bool isTxt2);
private void SetText(string text, bool isTxt2)
{
if (isTxt2)
{
if (this.textBox2.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
textBox2.Invoke(d, new object[] { text, isTxt2 });
}
else
{
textBox2.Text = text;
}
} else {
if (this.textBox3.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
textBox2.Invoke(d, new object[] { text, isTxt2 });
} else {
this.textBox3.Text = text;
}
}
}
public void sendData(IService1 patientSvc)
{
Patient patient = patientSvc.GetPatient(Convert.ToInt32(textBox1.Text));
if (patient != null)
{
SetText(patient.FirstName, true);
SetText(patient.LastName, false);
}
}
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'm working with a windows service where i want to add a GUI too it. I made a proof of concept with my service creating a ServiceHost object and hosting the WCF Named pipes service and then i Console application to consume the WCF service and also getting callback responses from the service (messages sent from server to connected clients). This works great my console application runs and get responses from the service with no interruption or delays.
However when doing the same thing in my WPF GUI application when clicking a button that then calls the WCF service it freezes the whole UI thread and then after a couple of minutes throws an exception and then the UI is updated with the message callback (server sends message to connected clients) but any return values from service is lost since the exception was thrown.
The two exception messages i have gothen is theses (the most common is the first):
1: The requesting action sent to net.pipe :/ / localhost / PipeGUI did not receive a response within the specified timeout (00:00:59.9989999). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Raise the deadline for action (by entering the channel / proxy to Icon Text Channel and set the property Operation Timeout) and verify that the service can connect to the client.
2: Communication object System.ServiceModel.Channels.ServiceChannel, can not be used for communication because it has been canceled.
Anyone got any ideas why this is happeing ?
I can post more code if neccessary.
UPDATE , added code for reference
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void MessageRecived(string message);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
public interface IPipeServiceContract
{
[OperationContract]
string Hello();
[OperationContract]
void Message(string msg);
[OperationContract(IsInitiating = true)]
void Connect();
[OperationContract(IsTerminating = true)]
void Disconnect();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, UseSynchronizationContext = false)]
public class PipeService : IPipeServiceContract
{
List<IClientCallback> _clients = new List<IClientCallback>();
public string Hello()
{
PublishMessage("Hello World.");
return "Return from method!";
}
public void Connect()
{
_clients.Add(OperationContext.Current.GetCallbackChannel<IClientCallback>());
}
public void Disconnect()
{
IClientCallback callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
_clients.Remove(callback);
}
void PublishMessage(string message)
{
for (int i = _clients.Count - 1; i > 0; i--)
{
try
{
_clients[i].MessageRecived(message);
}
catch (CommunicationObjectAbortedException coae)
{
_clients.RemoveAt(i);
}
catch(CommunicationObjectFaultedException cofe)
{
_clients.RemoveAt(i);
}
}
}
public void Message(string msg)
{
PublishMessage(msg);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged, IClientCallback
{
public ServiceController Service { get; set; }
protected IPipeServiceContract Proxy { get; set; }
protected DuplexChannelFactory<IPipeServiceContract> PipeFactory { get; set; }
public ObservableCollection<ServerActivityNotification> Activity { get; set; }
public override void BeginInit()
{
base.BeginInit();
PipeFactory = new DuplexChannelFactory<IPipeServiceContract>(this, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PipeGUI"));
}
public MainWindow()
{
InitializeComponent();
Activity = new ObservableCollection<ServerActivityNotification>();
Service = ServiceController.GetServices().First(x => x.ServiceName == "Server Service");
NotifyPropertyChanged("Service");
var timer = new DispatcherTimer();
timer.Tick += new EventHandler(OnUpdate);
timer.Interval = new TimeSpan(0, 0, 0, 0, 850);
timer.Start();
if (Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
void OnUpdate(object sender, EventArgs e)
{
Service.Refresh();
NotifyPropertyChanged("Service");
StartButton.IsEnabled = Service.Status != ServiceControllerStatus.Running ? true : false;
StopButton.IsEnabled = Service.Status != ServiceControllerStatus.Stopped ? true : false;
if (PipeFactory != null && Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private void OnStart(object sender, RoutedEventArgs e)
{
try
{
Service.Start();
}
catch
{
Service.Refresh();
}
}
private void OnStop(object sender, RoutedEventArgs e)
{
try
{
if (Proxy != null)
{
Proxy.Disconnect();
PipeFactory.Close();
}
Service.Stop();
}
catch
{
Service.Refresh();
}
}
public void MessageRecived(string message)
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
ServerActivityNotification log = new ServerActivityNotification { Activity = message, Occured = DateTime.Now };
Activity.Add(log);
ListBoxLog.ScrollIntoView(log);
NotifyPropertyChanged("Activity");
}));
}
private void OnHello(object sender, RoutedEventArgs e)
{
try
{
Proxy.Message(txtSendMessage.Text);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
Try setting the UseSynchronizationContext property of the service behaviour to false:
[ServiceBehavior(UseSynchronizationContext = false)]
class MyService
{
}
[ServiceContract]
public interface IMyService
{
}
I believe that by default this is set to true, so you are currently attempting to consume and run the WCF service on the same thread resulting in a deadlock.
In any case, it sounds like you are trying to consume the WCF service on the UI thread of the WPF application. Generally it is recommended that you perform potentially long running tasks on the background thread as this keeps the interface responsive even if your service call takes a few seconds/minutes.
EDIT:
I tried and succeeded in replicating your problem. Trying to call the service on the UI thread results in the UI freezing. However, when I changed my code to call the service on a background task (see below), I was able to call the service and receive the callback:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
var myService = DuplexChannelFactory<IMyService>.CreateChannel(new CallbackImplementation(),
new WSDualHttpBinding(),
new EndpointAddress(
#"http://localhost:4653/myservice"));
myService.CallService();
string s = "";
});
}
I have to confess, I am not entirely sure why this is so, and any clarification on exactly how WCF manages the thread hosting the service instance would be great in working out why this works.
Still struggling with the server part of my wcf application.
The problem is that the creating a "callback" object within the server class cased a "System.NullReferenceException was unhandled" error.
If I understand right, it happens when I create this server object - ServerClass myServer = new ServerClass();
So I guess I should somehow create a list for server objects and create & add this objects automatically whenever a client makes a connection. Please suggest, what would be the best way of doing that?
Here's my code so far:
namespace server2
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
myServer.eventHappened += new EventHandler(eventFunction);
}
ServerClass myServer = new ServerClass();
// here I instance a new server object.
// yet I believe that I need to make a list, that would store this objects, so that multiple clients would be able to connect,
// and I would be able to pick, to whom I want to send a callback.
void eventFunction(object sender, EventArgs e)
{
label1.Text = myServer.clientName;
}
private ServiceHost duplex;
private void Form2_Load(object sender, EventArgs e) /// once the form loads, create and open a new ServiceEndpoint.
{
duplex = new ServiceHost(typeof(ServerClass));
duplex.AddServiceEndpoint(typeof(IfaceClient2Server), new NetTcpBinding(), "net.tcp://localhost:9080/service");
duplex.Open();
this.Text = "SERVER *on-line*";
}
}
class ServerClass : IfaceClient2Server
{
public event EventHandler eventHappened;
IfaceServer2Client callback = OperationContext.Current.GetCallbackChannel<IfaceServer2Client>();
// ERROR: System.NullReferenceException was unhandled
public string clientName = "noname";
public void StartConnection(string name)
{
clientName = name;
MessageBox.Show(clientName + " has connected!");
eventHappened(this, new EventArgs());
// ERROR: System.NullReferenceException was unhandled :(
callback.Message_Server2Client("Welcome, " + clientName);
}
public void Message_Cleint2Server(string msg)
{
}
public void Message2Client(string msg)
{
}
}
[ServiceContract(Namespace = "server", CallbackContract = typeof(IfaceServer2Client), SessionMode = SessionMode.Required)]
public interface IfaceClient2Server ///// what comes from the client to the server.
{
[OperationContract(IsOneWay = true)]
void StartConnection(string clientName);
[OperationContract(IsOneWay = true)]
void Message_Cleint2Server(string msg);
}
public interface IfaceServer2Client ///// what goes from the sertver, to the client.
{
[OperationContract(IsOneWay = true)]
void AcceptConnection();
[OperationContract(IsOneWay = true)]
void RejectConnection();
[OperationContract(IsOneWay = true)]
void Message_Server2Client(string msg);
}
}
Thanks!
This can not be done that way. First of all, the Callback Channel is only available within an operation, not when the instance of the service class is created.
Secondly, the instances of your ServerClass are created by the WCF service host depending on your WCF configuration. For example, there may be one instance per call (!). So creating an instance yourself and attaching an event handler does not affect the automatically created instances. That's why you get an exception in StartConnection.
What I'd do in that case is:
Create a singleton class that publishes the desired event
Attach a handler to the event from within your main code. This will be the code that listens to events
Create a public method (like ConnectionStarted) which raises the event
Call this method from your ServerClass
If you don't need to wait for the event handler to finish, you can also raise the event asynchronously in a separate thread. You then have to make sure that the event handler attached in step 2) handles thread contexts properly using for example this.Invoke (Forms) or this.Dispatcher.BeginInvoke (WPF).
Try putting that line in the class constructor:
class ServerClass : IfaceClient2Server
{
public event EventHandler eventHappened;
IfaceServer2Client callback;
public ServerClass ()
{
callback = OperationContext.Current.GetCallbackChannel<IfaceServer2Client>();
}
...
}
If still no luck it probably means you can use OperationContext.Current only inside some operation.
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;
...
...
}