How to integrate topshelf to an existing windows service project? - c#

I want to be able to use the TopShelf debugging abilities of my service in Visual Studio.
A lot of the examples and documentation out there refer to creating a Windows Console project in Visual Studio first, and then adding TopShelf, OWIN, etc
However, in my case I already have a perfectly good and working Windows Service project called QShipsService.sln, etc... and it uses a simple Connected Service (admittedly to old SOAP legacy services).
Can someone please direct me or provide an example of how to use TopShelf, with an existing non-Console like project?

I found my own solution...
The assumption I made was the default Windows Service project defaulting to wanting to register the program as a service and kick off the OnOpen() and OnClose() methods, once the service is running.
In my case I wanted to re-use an existing service that was based on a Timer(), and it would kick in every 4 hours to call a SOAP call and return some data. What I didn't realise was the ServiceConfigurator was trying to call its own Open() and Close() methods.
So I commented out the OnOpen and OnClose methods and allowed the configurator to call my worker process via Open() method instead, which is what I was meant to have done the first time!
For the noobs out there like me, here is the code...
//using System.ServiceProcess;
using Topshelf;
namespace QShipsService
{
static class Program
{
static void Main(string[] args)
{
HostFactory.Run(
configure =>
{
configure.Service<QShipsService.QshipsService>(
service =>
{
service.ConstructUsing(s => new QShipsService.QshipsService());
service.WhenStarted(s => s.QStart());
service.WhenStopped(s => s.QStop());
});
//Setup Account that window service use to run.
configure.RunAsLocalSystem();
//add details and names about the service
configure.SetServiceName("QshipsService");
configure.SetDisplayName("QshipsService");
configure.SetDescription("QshipsService Windows Service to extract data from the QSHIPS SOAP service. Data is recorded and maintained inside the SPOT's database in POT-DB.");
});
//## USE THIS IF WE'RE NOT USING TOPSHELF !! ##
// //this loads and starts the QshipsService (see QshipsService.cs program)
// ServiceBase[] ServicesToRun;
// ServicesToRun = new ServiceBase[]
// {
// new QShipsService.QshipsService()
// };
// ServiceBase.Run(ServicesToRun);
}
}
}

Related

C# Windows service won't start: "Cannot start service from command line or debugger"

Even though there are similar questions, I couldn't find any that solves mine. I have a simple program that runs as a service and I want to start it programatically. It's as simple as this:
private static void StartService()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
As expected, I can't just start my service without installing it. Windows gives me the following error message:
Cannot start service from command line or debugger. A windows service must first be installed using installutil.exe and then started with service explorer, Windows services administrative tool or NET start.
So far so good. So I went there and did just as the docs says:
installutil <my_project>.exe
The installation was successful and I can even start my service from Service Manager or net start. The only problem is: when I debug my application (via F5), Windows keeps showing me the exact same message: Cannot start service (...).
I've found a solution here that uses this:
public void onDebug()
{
OnStart(null);
}
Which allows me to run and debug my application normally, but I actually need it to run as a service and Windows refuses to start that way. Is there anything I'm missing?
It is not in your power to just start a Service like a normal programm. The Service must be registered with and started by the Service manager. That is one of the (many) rules of Windows services. And you have to repeat that for every new build.
As this and other Service related rules (no interactive sessions) can make developing them a Pain, a common approach is to develop them using a console application. I could not find my ideal example, but I found something like it:
https://alastaircrabtree.com/how-to-run-a-dotnet-windows-service-as-a-console-app/
Of course a better longterm plan might be to stop using Services alltogether and switch over to the Windows Task Scheduler. It depends heavily on what exactly you need this code to be able to do in practice.

How should a GRPC Service be hosted?

I have created a GRPC Server in C# using the example given at Link. Now I want to figure out as how should I be hosting this server so that I achieve following:
Should I make this Server a Console application or a a Windows Service. If I make it a windows Service then updating the service will be cumbersome (which is a big negative) and if I make it a console app then updating will simply need shutting down exe. But that comes with the price of closing the same by mistake. Is there any other better way?
With IIS this issue won't b there as I can simply remove the site from LB and stop the website to perform the update but since GRPC won't be a part of IIS, I am not sure what's the way to get this working.
Any references for the better architecture are welcomed.
We can use Microsoft.Extensions.Hosting pacakge to host a .net core console application by using the HostBuilder API to start building gRPC host and setting it up.
In order to run the gRPC service, we first need to start/stop Grpc.Core.Server in a hosted service. A hosted service is basically a piece of code that is run by the host when the host itself is started and the same for when it is stopped. The following code implement a GrpcHostedService to override IHostedService interface:
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Hosting;
namespace Grpc.Host
{
public class GrpcHostedService: IHostedService
{
private Server _server;
public GrpcHostedService(Server server)
{
_server = server;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_server.Start();
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken cancellationToken) => await _server.ShutdownAsync();
}
}
In the Program.cs, use HostBuilder API to start building our grpc host and setting it up:
public class Program
{
public static async Task Main(string[] args)
{
var hostBuilder = new HostBuilder()
// Add configuration, logging, ...
.ConfigureServices((hostContext, services) =>
{
// Better to use Dependency Injection for GreeterImpl
Server server = new Server
{
Services = {Greeter.BindService(new GreeterImpl())},
Ports = {new ServerPort("localhost", 5000, ServerCredentials.Insecure)}
};
services.AddSingleton<Server>(server);
services.AddSingleton<IHostedService, GrpcHostedService>();
});
await hostBuilder.RunConsoleAsync();
}
}
By doing this, the generic host will automatically run StartAsync on our hosted service, which in turn will call StartAsync on the Server instance, essentially start the gRPC server.
When we shut down the host with Control-C, the generic host will automatically call StopAsync on our hosted service, which again will call StopAsync on the Server instance which will do some clean up.
For other configuration in HostBuilder, you can see this blog.
I'm going to add one more option.
With dot net core, you can run this as a Linux Daemon now.
Currently gRPC doesn't support integration with ASP.Net/IIS. You would need to host the server in a console or as a Windows service.
Likely you would want this to be a Windows service to make it easier to keep the server running across reboots or crashes. If you want to easily turn your console application into a Windows service I would recommend using the excellent TopShelf Nuget.
Updating the service can be done as you would a console app.
Stop the Windows service. net stop <service-name}>
Copy the updated assemblies.
Start the Windowsservice net start <service-name>
My company (Shortbar) is building the application server for a hotel management system called HOLMS on gRPC. Our setup is as follows:
HOLMS.Application is a .NET class library (assembly) that does the actual work of the server
HOLMS.Application.ConsoleRunner is a C# console application that hosts HOLMS.Application. The console runner is used by (1) developers for convenience (mentioned in the question) as well as (2) production scenarios running inside a Docker container, where the container runtime (e.g. Amazon ECS) implements job control/scaling. It follows "12 factor app" guidelines, including running itself as a single, standalone, stateless process, fast startup/shutdown, and environment-variable config injection. The system logs to stdout which gets drained however stdout is drained in the prod environment (e.g. Sumo, logstash, etc). This is how our SaaS multi-tenant solution will go into production.
HOLMS.Application.ServiceRunner packages HOLMS.Application into a Windows service, for more traditional, on-premise situations where a customer's IT group will run the service themselves. This package uses the Windows registry for configuration and relies on Windows service job control for startup/shutdown/restarts. It logs to the Windows Event Log.
The ConsoleRunner and ServiceRunner each are only about 200 lines of code; for the most part, they just wrap the Application package, and call into it.
Hope this helps.

Call Web Services from Windows Service

I created a Web Service where I take data from a database and insert
them into a list of string type. This web service is called from a
Windows Service that receives the list and retrieving the data. To do
this I added the reference to the Windows Service, but I do not know
if I get the list from the Web Service correctly. This is the code of
the windows service:
RicDati ricdati = new RicDati();
var listas = ricdati.PrelevaDati().Count();
List<string> lista = new List<string>();
lista.AddRange(ricdati.PrelevaDati());
RicDati is the class of the Web Service, PrelevaDati is the name of the method
I think is more a matter of debugging your windows service, sometimes this could be very difficult but ic an suggest a workarround.
Practically you need to create a console application or windows that practically is going to create and call a windows service instance without even intall it on your computar, this practically feels like you're debugging a normal windows application.
this is a project that uses a winform to debug win services I'm more into a consolo (because I feel it's easier), but the concept is the same so you can take a look.
enter link description here
the main code practically is the following:
using System.ServiceProcess;
using ServiceProcess.Helpers;
namespace DemoService
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
//ServiceBase.Run(ServicesToRun);
ServicesToRun.LoadServices();
}
}
}
Before running it in a Service, run/debug the parts which are not service related in a console application.
Create a library project of your business processes which the service will use, but also the console application. This will contain the call to the webservices.
Test the process in the console without having the unknowns of a service.
Once the processes are working then run it in the service.

Running Windows Service Application without installing it

Whem I'm writing a Windows Service and just hit F5 I get the error message that I have to install it using installutil.exe and then run it.
In practice this means everytime I change a line of code:
compile
switch to Developer Command Prompt
remove old version
install new version
start service
That is very inconvenient. Is there a better way to do it?
The best way in my opinion is to use Debug directive. Below is an example for the same.
#if(!DEBUG)
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
// Calling MyService Constructor
new MyService()
};
ServiceBase.Run(ServicesToRun);
#else
MyService serviceCall = new MyService();
serviceCall.YourMethodContainingLogic();
#endif
Hit F5 And set a Breakpoint on your YourMethodContainingLogic Method to debug it.
I usually put the bulk of the service implementation into a class library, and then create two "front-ends" for running it - one a service project, the other a console or windows forms application. I use the console/forms application for debugging.
However, you should be aware of the differences in the environment between the debug experience and when running as a genuine service - e.g. you can accidentally end up dependent on running in a session with an interactive user, or (for winforms) where a message pump is running.
You can write this code in program.cs
//if not in Debug
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
//if debug mode
MyService service = new MyService();
service.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
in MyService class
public void OnDebug()
{
OnStart(null);
}
You cannot run Windows Service as say another console or WinForms application. It needs to be started by Windows itself.
If you don't have infrastructure ready to use as #Damien_The_Unbeliever suggests (which is what I recommend as well) you can install the service from the debug location. So you use installutil once and point it to executable located in /bin/debug. Then you start a service from services.msc and use Visual Studio > Debug > Attach to Process menu and attach to the Windows service.
You can also consider using Thread.Sleep(10000) as the first line in the OnStart call, or Debugger.Break() to help you out to be able to attach before the service executes any work. Don't forget to remove those before the release.
You can use Environment.UserInteractive variable. Details of implementation here
Here's an easy way I use to debug Windows service applications without installing them, starting via Windows Service Control Manager, attaching to debuggers, etc. The following is in VB but hopefully you get the idea.
In this example, TestService's main class is named svcTest.vb.
Within Shared Sub Main() inside svcTest.Designer.vb, the default code looks something like this:
Dim ServicesToRun() As System.ServiceProcess.ServiceBase
ServicesToRun = New System.ServiceProcess.ServiceBase() {New svcTest}
System.ServiceProcess.ServiceBase.Run(ServicesToRun)
Comment everything out within Main() and add the following 2 lines of code.
Dim objSvc As New svcTest()
objSvc.OnStart(Nothing)
Now just set a breakpoint where you want to start debugging, hit F11 to step into the code, and then proceed as normal as if you were working with a standard desktop application. When you're finished debugging, simply reverse the changes made within Main().
This was done using Visual Studio Enterprise 2017 on Windows Server 2012 R2.

MSDN WCF service example not working

This question is a followon from my previous where I have since discovered that it is not working 100% on my computer: WCF service not running on non-dev machine
I'm working through this example:
http://msdn.microsoft.com/en-us/library/ff649818.aspx
It turns out that the InstallUtil step isn't really working here. I've discovered that if VS2010 has the project open, and you go to add a service reference like in step 8 of the tutorial, VS2010 actually starts up the service host and therefore a reference is created.
Here's how i've debugged so far:
Install the service as per InstallUtil, close down VS2010 solution; then open a completely different solution (TESTWCF) Try and add a service reference and it fails - cannot find at the specified address
Open WCFServiceLibrary1 project again as a separate instance of VS2010. Try and add a service reference to TESTWCF and it fails.
Within WCFServiceLibrary1, attempt step 8 - add a service reference. This causes the service host to start and the service is found.
With service host still running, in TESTWCF I then try and add service and it works.
Close down the service host and try and add reference in TESTWCF and it doesn't work again.
This all seems to be totally independant of the service running or not running as installed by InstallUtil.
I've also verified this through the creation of a new virtual server from scratch and loading things on one by one. And only when VS2010 was installed did it start to work - when I observed above.
Any ideas ?
WCF services can be self-hosted in an application (such as a console or a Windows Forms application)
I think you are over complicating it, you don't have to even install it with InstallUtil.
InstallUtil installs it to run as windows service, and you can make console application which will be serving as WCF service.
You have to import:
System.ServiceModel
System.ServiceModel.Web
System.Web.Services
I think those with web will be needed if you want to use it as web service with get and post.
Then you need to specify contract for client and server.
[ServiceContract(Name = "SomeService", Namespace = "http://some.domain.com/some/someservice",SessionMode=SessionMode.Required)]
public interface ISomeService
{
[OperationContract]
string Execute(string expression);
}
You have contract and now you have to implement it in service. nothing special in there just use this interface.
What is very important is app.config, you have to specify it well for client and for service. In config you have all stuff that points to service.
In client you have to add service as reference, it should find it as in point 8 but only if you have configs ok!
In client just do something in code like that:
using (ChannelFactory<ISomeService> channel = new ChannelFactory<ISomeService>("SomeService"))
{
ISomeService svc = channel.CreateChannel();
svc.Execute("my expression to evaluate by the service");
}
Try to make it easiest possible way without InstallUtil and such, it doesn't have to be windows service to serve stuff over network.
Success ! After like 4 days of effort on this, the MSDN tutorial has a fatal flaw.
In the first step of the tutorial you create a wcf service library and by default it names the service Service1. In step 2.6 of the tutorial you are asked to specify the base address:
net.tcp://localhost:8523/Service1
Step 3 you are asked to create a new windows service, and by default this is also called Service1.
In step 5.2 you are asked to make a reference to System.ServiceModel and to WcfServiceLibrary1.
In step 5.6 you replace the Onstart Method to start the service and, Step 8 shows the final code as being:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.ServiceModel;
using WcfServiceLibrary1;
namespace WindowsService1
{
public partial class Service1: ServiceBase
{
internal static ServiceHost myServiceHost = null;
public WCFServiceHost1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(Service1));
myServiceHost.Open();
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
}
The crucial line of code which is wrong is:
myServiceHost = new ServiceHost(typeof(Service1));
Well it might behave differently in VS2008 or 2005 or maybe it's a config in VS2010 however, my VS2010 interprets Service1 to be that of the containing class ie:
WindowsService1.Service1
Whereas it should in fact be:
WcfServiceLibrary1.Service1
I noticed that 4 days ago but figured I didn't know enough about WCF and I was wrong somehow - esp when it appeared to work 'cause of VS2010 starting it up itself.

Categories