2
votes

I have an existing Windows Service that starts up, reads some configs, spins up some threads and does some useful work. Currently I log that work (through TopShelf.Log4Net / Log4Net) to disk (or console).

I was thinking it would be nice to expose an API to allow people to query the service (and in the future maybe even config it on the fly)

I'm having difficulty figuring out an appropriate way to plumb the two together though. My existing windows service has a bunch of worker threads and it knows the context of what work is being done, stats and progress and things like that.

But in the context of an ApiController actually handling a request I can't see an obvious/easy means of getting at those stats. I tried passing some object references in the Properties of the IAppBuilder in my Startup class, but any time I explicitly do the config myself I seem to lose the default MVC routes that I had.

Anyone have any suggestions on integrating OWIN into/on top of an existing service?

EDIT: Added code:

So I have a very basic TopShelf runner;

        HostFactory.Run(x =>
            {
                x.Service<MyService>(s =>
                {
                    s.ConstructUsing(name => new MyService());
                    s.WhenStarted(lb => lb.Start());
                    s.WhenStopped(lb => lb.Stop());
                });
                x.RunAsLocalSystem();

And within MyService I had a method StartWorkers() that goes off, starts up a bunch of workers, and keeps track of them.

public class MyService
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    private List<Worker> workers { get; set; }
    private List<Thread> _threads { get; set; }

    public MyService()
    {
        StartWorkers();
    }

Now, I want to be able to query for the status of those workers from API requests, so I was thinking I could do something like;

public class MyService
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    private List<Worker> workers { get; set; }
    private List<Thread> _threads { get; set; }
    private IDisposable _webServer { get; set; }

    public MyService()
    {
        StartWorkers();
        StartWebServer();
    }

    private void StartWebServer()
    {
        const string baseAddress = "http://localhost:10281/";
        _webServer = WebApp.Start<Startup>(url: baseAddress);
        log.DebugFormat("Loaded webserver at address={0}", baseAddress);
    }

All of the OWIN code right now is vanilla from the samples. When I test this code, I can hit the /api/values sample endpoint which is served from the ValuesController, and all is well.

Now where I'm missing a piece of glue is how to access anything useful in my application from my controller. I can do something naive like making the Worker list public static;

public class MyService
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    public static List<Worker> workers { get; set; }

In which case from my controller I can do something like;

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        // nonsense/test code
        return MyService.workers.Select(i => i.ErrorsEncountered.ToString()).ToArray();
    }

There must be a better way of access the internals of my application without manipulation visbility. What I've seen is that I can pass objects into the Properties of the IAppBuilder when using WebApp.Start(). These are visible then from the route configuration, and I see to be able to access them from within my ApiController, e.g.;

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        var someWorker = base.ControllerContext.Configuration.Properties["someWorkerReference"];

The problem I have when I go down that route is that 'WebApp.Start(url: baseAddress);' works and my routes all function, but using the WebApp.Start() method and passing in an Action causes my routes to break

So instead of spending time fixing the routes, I wanted to see if there's an obvious/known/official means of passing "context" into the web server

1
Can you add some code and/or config demonstrating the problem?stuartd
Sure thing stuartd - I guess I was being somewhat vague alright. When I get home I'll lob in some codeplenderj

1 Answers

0
votes

So this is where having a container is super helpful. If you have something like public class MyService : IStatusProvider {} then you can register both MyService and IStatusProvider to the same instance you. How to use DI container when OwinStartup talks about using OWIN & dependency injection. And you would add the container setup to start of the program, changing s.ConstructUsing(name => new MyService()); to

s.ConstructUsing(name => {
    var container = new Container();
    // container setup
    return container.resolve<MyService>(); // method depends on your container
});