Using SqlDependency in WCF Class Library project hosted as Windows Service - c#

I have created a notification service using SqlDependency for notifying other services about the change in a particular table. This is done in the Windows Service template from Visual Studio. Hence in the OnStart event, subscription of SqlDependency is created, and during OnStop event, un-subscription is done.
The above solution is working perfectly fine.
Now, due to some reasons, I have to make this a WCF class library project and host it as a Windows service. I couldn't think of a place where the subscription and un-subscription of SqlDependency can be done in such case.
When the Windows service is started, the subscription has to be done automatically.
Note: I have a generic Windows service host, which will host all my WCF class library project and can't do much there.
Is there any solution or workaround for this?

I hope that I got your problem right. You have all the logic inside of WCF library but don't know how to sync your subscribe and unsubscribe to SqlDependancy events on Windows Service start-up that will host your WCF service.
My idea is to use a ServiceHostFactory to create an instance of your service and hook on opening and closing events of your service, calling all necessary constructors and connectors from there.
I based most of my answer from this great and lengthy article about hosting WCF services, with link pointing directly to the topic in your case. Some additional docs on ServiceHost and ServiceHostFactory classes.
Bear in mind that this is not a complete code that you can just copy/paste but rather a demonstration of usage.
Here is an example of your Windows service:
public partial class YourWindowsService : ServiceBase
{
// It's your choice where to create this instance, I used constructor injection here arbitrarily
private readonly YourWCFServiceFactory serviceFactory;
private ServiceHost host;
public YourWindowsService(YourWCFServiceFactory serviceFactory)
{
InitializeComponent();
this.serviceFactory = serviceFactory;
}
protected override void OnStart(string[] args)
{
Type serviceType = typeof(YourService);
host = serviceFactory.CreateServiceHost(serviceType, new string[] { "yourBaseUri" });
host.Open();
}
protected override void OnStop()
{
if(host != null)
host.Close();
}
}
And an example of your factory:
public class YourWCFServiceFactory: ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
host.Opening += new EventHandler(host_Opening);
host.Closing += new EventHandler(host_Closing);
return host;
}
private void host_Opening(object sender, EventArgs e)
{
// Initialization here
}
private void host_Opening(object sender, EventArgs e)
{
// Cleanup here
}
}

Related

How to communicate between a windows service and windows form with a WCF?

I have a Timeout after this timeout I have a send a message between windows service and windows form with a WCF.
I can already send a message from windows form to a windows service :
WCF
public class Interact : IInteract
{
private Func<string, int> callback;
public Interact(Func<string, int> callback)
{
this.callback = callback;
}
public void SendRequest(string name)
{
var output = this.callback(name + " callback");
}
public string AskIfAlive()
{
return "ask";
}
}
Service windows
public partial class LMService : ServiceBase
{
private ServiceHost host;
public LMService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Class1.Init();
Interact instance = new Interact(ReceiveMsg);
host = new ServiceHost(instance);
host.Open();
}
private int ReceiveMsg(string data)
{
// Message from Windows form to windows service
Log.writeEventLog(data);
return 1;
}
}
Timer
public static class Class1
{
static int Timeout = 0;
static Timer tm = new Timer();
public static void Init()
{
tm.Interval = 1000;
tm.Elapsed += Tm_Elapsed;
tm.Start();
}
private static void Tm_Elapsed(object sender, ElapsedEventArgs e)
{
Timeout++;
if(Timeout >= 10)
{
// SEND MESSAGE TO WINDOWS FORM
tm.Stop();
}
}
}
I want to send something to the windows form after the timeout in windows service but I don't know how to do it someone can help me ?
You can use a Duplex Service to allow for the service to also send messages to the Windows Forms app.
See this link for details and a sample.
A duplex service is a WCF service that receives a callback that it can use to send messages to the client. In addition to the service contract, you also need to create a callback contract that is used by the service.
Please note that the callback is tied to the WCF operation context. You might need to change the static Init method of your sample to an instance method and create a new instance of Class1 per call/client.
Whether the service we designed is duplex or request/response model. The initiator of the service is client-side, therefore we should host the service in Windows form application instead of the Windows NT service.
Besides, your Windows NT service seems to send a message to the Windows Form application once. Therefore, I don’t think it is necessary to use the duplex service.
About the duplex service, I have ever made an example. Wish it is useful for you.
TimeOut exception in WCF while implementing duplex
Feel free to let me know if there is anything I can help with.

How to send strings to a WinForms application from another application using methods

I have an EXE that I've created called logger which is a simple WinForms application. It has a richtextbox and that's pretty much it.
I then also have a suite of other applications. What I want to be able to do with these apps is to be able to get them to write output to the logger.exe I have full control over the code of all applications.
I understand I could do a process.start and specify arguments but I want these apps to be able to write to the richtextbox at will based on the methods being called within them.
I was hoping I could create an api in the logger.exe that would expose a method for appending the richtextbox.
Does anyone have any tips on how I might achieve this?
EDIT: This is what I have so far:
namespace ScreenLog
{
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger : Form, IFromClientToServerMessages
{
public Logger()
{
InitializeComponent();
}
public void DisplayTextOnServerAsFromThisClient(string text)
{
LogConsole.AppendText(Environment.NewLine + text);
}
}
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
}
As you might have already guessed you would need any of IPC(Inter Process Communication) mechanism to send messages between different processes(Applications). WCF is one of the option, You could implement a simple WCF service module which uses net.pipe binding. This service can be hosted in managed application. In your case this service can be hosted in your logger application.
Note: If you want to host a WCF application in a managed application, Particular managed application(Logger) should have admin privilege.
Implementation of Logger Form
partial class declaration
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger: Form, IFromClientToServerMessages
Introduce Interface for communication
This interface should be added to a assembly which is accessible by both Logger application and any other application which sends message to logger.
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
Implementing Interface
Add the following method implementation to your Logger form
public void DisplayTextOnServerAsFromThisClient(string text)
{
//Add proper logic to set value to rich text box control.
richtextbox = text;
}
Hosting the WCF service in logger application
Invoke HostTheNetPipeService() within the constructor of Logger Form
private void HostTheNetPipeService()
{
serverHost = new ServiceHost(this);
serverHost.AddServiceEndpoint((typeof(IFromClientToServerMessages)), new NetNamedPipeBinding(), "net.pipe://127.0.0.1/Server");
serverHost.Open();
}
Call the service from other applications to send message/text
private void SendMessageToLogger()
{
using (ChannelFactory<IFromClientToServerMessages> factory = new ChannelFactory<IFromClientToServerMessages>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/Server")))
{
IFromClientToServerMessages clientToServerChannel = factory.CreateChannel();
try
{
clientToServerChannel.DisplayTextOnServerAsFromThisClient("Message to be displayed");
}
catch (Exception ex)
{
}
finally
{
CloseChannel((ICommunicationObject)clientToServerChannel);
}
}
}
Closing the communication channel
private void CloseChannel(ICommunicationObject channel)
{
try
{
channel.Close();
}
catch (Exception ex)
{
}
finally
{
channel.Abort();
}
}

Running a function on WCF start up

I'm not sure if its possible, but I'd like to have a function run as soon as a WCF service is started to generate initial cache data. I'm not worried now about how to implement the cache, my question is strictly about having the function run when the service starts. The service will be RESTful.
The service will eventually be hosted in IIS and is using .Net Framework 4.5
The easiest way is to create an App_Code folder underneath your WCF project root, create a class (I'll call it Initializer but it doesn't matter. The important part is the method name) like so:
public class Initializer
{
public static void AppInitialize()
{
// This will get called on startup
}
}
More information about AppInitialize can be found here.
What #KirkWoll suggested works, but only if you're in IIS and that's the only AppInitialize static method under App_Code. If you want to do initialization on a per-service basis, if you have a different AppInitialize method or if you're not under IIS, you have these other options:
If using .NET Framework 4.5, and under IIS: You can use the service configuration method which will be called when the service is running. More info at http://msdn.microsoft.com/en-us/library/hh205277(v=vs.110).aspx.
If you're self-hosting your service, you control when the service starts (the call to ServiceHost.Open(), so you can initialize it there
If you're under IIS, and not on 4.5, you can use a service host factory and a custom service host to be called when the service host is being opened. At that point you can do your initialization. You can find more about service host factories at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx.
An example of a custom factory is shown below:
public class MyFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
host.Opening += new EventHandler(host_Opening);
return host;
}
void host_Opening(object sender, EventArgs e)
{
// do initialization here
}
}
}
In my case I did like below. I have Windows service project that hosted a WCF Rest service. I wrote below code in my windows service project MyService.cs
protected override void OnStart(string[] args)
{
try
{
ServiceHost myServiceHost = new ServiceHost(typeof(myservice));
myServiceHost.Opening += OnServiceHostOpening;
myServiceHost.Open();
}
catch (Exception ex)
{
//handle exception
}
}
private void OnServiceHostOpening(object sender, EventArgs e)
{
//do something
}

WCF: Accessing the service instance from the server

Context:
I need to develop a monitoring server that monitors some of our applications (these applications are in c#). So I decided to develop the system with WCF which seems suitable for my needs.
These applications must register themselves to the monitoring server when they start. After that the monitoring server can call the methods Start or Stop of these applications.
Everything is completely executed on the same machine, nothing needs to be executed remotely.
So I developed a good prototype and everything works fine. Each application registers itself to the monitoring server.
Question:
ApplicationRegistrationService (see the code below) is the implementation of the monitoring service and it is a singleton instance due to the ServiceBehavior attribute.
Here my problem: I want to access the content of ApplicationRegistrationService per example, the number of connected applications from my server (ConsoleMonitoringServer in the example). But, I am not sure how to achieve this.
Do I need to create a channel in my server to the service like I did in my clients (ConsoleClient) or it exists a better way to achieve this?
Code:
The code is very simplified for the purpose of this question:
//The callback contract interface
public interface IApplicationAction
{
[OperationContract(IsOneWay = true)]
void Stop();
[OperationContract(IsOneWay = true)]
void Start();
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IApplicationAction))]
public interface IApplicationRegistration
{
[OperationContract]
void Register(Guid guid, string name);
[OperationContract]
void Unregister(Guid guid);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ApplicationRegistrationService : IApplicationRegistration
{
//IApplicationRegistration Implementation
}
public class ApplicationAction : IApplicationAction
{
//IApplicationAction Implementation
}
Console application for this example
class ConsoleClient
{
static void Main(string[] args)
{
ApplicationAction actions = new ApplicationAction();
DuplexChannelFactory<IApplicationRegistration> appRegPipeFactory =
new DuplexChannelFactory<IApplicationRegistration>(actions,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/AppReg"));
IApplicationRegistration proxy = appRegPipeFactory.CreateChannel();
proxy.Register(Guid.Empty, "ThisClientName");
//Do stuffs
}
}
Console server for this example
class ConsoleMonitoringServer
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
{
host.AddServiceEndpoint(typeof(IApplicationRegistration),
new NetNamedPipeBinding(), "AppReg");
host.Open();
//Wait until some write something in the console
Console.ReadLine();
host.Close();
}
}
}
Finally, I find the answer and it was pretty easy. I just need to create the service instance and pass the reference to the constructor of ServiceHost.
So I need to replace the following code:
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
by :
ApplicationRegistrationService myService = new ApplicationRegistrationService();
using (ServiceHost host = new ServiceHost(myService,
new Uri[]{ new Uri("net.pipe://localhost")}))
If you mean you'd like two way communication between your monitoring service and your registered services or nodes, then you probably should be using two way communication in WCF also known as duplex communication. Very cool stuff.
http://www.codeproject.com/KB/WCF/WCF_Duplex_UI_Threads.aspx

WCF: Using my "custom" ServiceHost in my app.config/web.config for my wcf service?

I have a created a simple Custom ServiceHost that inherits from ServiceHost and overrides the method InitializeRuntime.
How do i change my app.config / web.config to use the custom service host so my overriden InitializeRunTime is executes..
I see attributes in the config file like behaviorConfiguration etc but nothing obvious where i can force it to use my Custom ServiceHost
My ServiceHost is simple :-
public class UnityServiceHost : ServiceHost
{
protected override void InitializeRuntime()
{
//DO MY UNITY INJECTION HERE
//Console.WriteLine("");
base.InitializeRuntime();
}
}
Any help really appreciated.
Thanks
Oran Dennison describes how to do this using Spring.NET: http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html
In summary, you'll use WCF's "behavior injection" to supply an instance of the service created by your DI container.
1) Create custom IInstanceProvider implementation with the GetInstance method returning the service object created by your container:
public object GetInstance(InstanceContext instanceContext, Message message)
{
return _container.Resolve(_serviceType);
}
2) Implement a custom IServiceBehaviour that adds your custom IInstanceProvider to each endpoint configuration.
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider =
new YourCustomInstanceProvider(serviceDescription.ServiceType);
}
}
}
}
3) In your custom service host, override OnOpening and add your custom service behavior
protected override void OnOpening()
{
this.Description.Behaviors.Add(new CustomServiceBehavior());
base.OnOpening();
}
Note that you may have to pass down your UnityContainer instance through to the IInstanceProvider so that it can do the resolving.
You need to create a custom ServiceHostFactory and use that to create your UnityServiceHost. You specify the ServiceHostFactory to use in the SVC file. See the MSDN docs for the SVC syntax.
If you're hosting the service yourself (as opposed to using IIS or WAS), you should simply create a UnityServiceHost instead of a regular ServiceHost.
If you are using IIS or WAS, write a ServiceHostFactory

Categories