Reputation: 4223
I have a Windows Service which kicks off a function on a timer. The function queries a database for new records and then calls a web service for each new record it finds.
There is now a requirement that this service does this for multiple databases at each interval.
The service currently looks like this:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ServiceProcess;
namespace MiManager.TicketLogger {
public partial class TicketLogger : ServiceBase {
private TicketLoggerSettings _settings;
private List<DbConnectionSettings> _connections;
public TicketLogger() {
InitializeComponent();
}
protected override void OnStart(string[] args) {
var helpers = new MiManager.TicketLogger.Helpers();
_settings = helpers.GetTicketLoggerSettings();
_connections = helpers.GetDbConnectionSettings();
if (_settings.LogVerbose == true) evlgTicketLogger.WriteEntry("Registry entries obtained", EventLogEntryType.Information, 100);
tmrTicketLogger.Interval = _settings.ServiceInterval;
tmrTicketLogger.Elapsed += new System.Timers.ElapsedEventHandler(TimerElapsed);
tmrTicketLogger.Enabled = true;
tmrTicketLogger.Start();
}
protected override void OnStop() {
tmrTicketLogger.Stop();
tmrTicketLogger.Enabled = false;
}
private void TimerElapsed(Object sender, System.Timers.ElapsedEventArgs e) {
try {
tmrTicketLogger.Stop();
Core core = new MiManager.TicketLogger.Core();
List<Message> messages = core.LogTickets(_settings);
foreach (var message in messages) {
evlgTicketLogger.WriteEntry(message.Text, message.EntryType, message.ErrorCode);
}
if (_settings.LogVerbose == true) {
evlgTicketLogger.WriteEntry("Ticket logging complete", EventLogEntryType.Information, 101);
}
}
catch (Exception ex) {
evlgTicketLogger.WriteEntry(ex.ToString(), EventLogEntryType.Error, 400);
}
finally {
tmrTicketLogger.Start();
}
}
}
}
Now I need to adjust the service to fetch multiple connection strings and call the LogTickets function in the TimerElapsed function for each db connection.
What is the current recommended way of doing this in a Windows Service?
In a previous project many years ago I manually created a new thread for each connection I wanted to process but I'm sure there's a better way now?
Is it a good use case for Async/Await? If I do that do all the methods in the Core class have to be async as well?
Would it be better to use something like Parallel.ForEach?
Upvotes: 4
Views: 8420
Reputation: 456377
On the server side, async
methods give you scalability. However, it sounds like your service doesn't need to scale much, so async
wouldn't really give you any advantage.
Would it be better to use something like Parallel.ForEach?
If your existing code is synchronous (and you don't need the scalability that async
would give you), then that's fine. Your service would be less resource-intensive (and probably slightly faster) if you change everything to async
- but that would require quite a bit of code changes for (AFAICS) little benefit.
If you do want to explore async
code in a Win32 service, I recommend creating a single main thread using my AsyncContextThread
type. As long as your async
code is properly asynchronous (e.g., using EF6 for asynchronous DB calls and HttpClient
for asynchronous web calls), then you should only need the one thread.
Upvotes: 4
Reputation: 97
Much depends on the sophistication needs of your solution, how you want to handle your data and what is your current .NET framework version because there are many ways to do this.
You can do something like this where HandleConnection is your function for handling the connection:
var parallelTask = Task.Factory.StartNew(() => Parallel.ForEach(_connections, connection => HandleConnection(connection)));
I would recommend using a few minutes to read up on parallelism, new async/await features in .NET 4.5 and tasks on MSDN. There are many different continuation options that can be used with tasks. Explore what suits your needs.
Asynchronous Programming with Async and Await
Upvotes: 0