Wednesday 29 May 2013

Problem running a Windows service with Topshelf and Spring.Net

Problem

I had written an application using Spring.Net for dependency injection - and some of the other features it provides - and Topshelf. The application could then be written as a console application and then installed and run as a Windows service using Topshelf’s handy ‘install’ command line parameter.
I was using XML files to configure Spring.Net. This turned out to be significant.

The application worked sweet as a nut as a console application and installed successfully as a Windows service. However, when I tried to run the Windows service using net start all I got was “The service is not responding to the control function”.




Solution

In the app.config file I had a spring configuration section that referenced external files for the spring.context:

<spring>
    <context>
      <resource uri="file://Config/SpringContext.xml" />
      <resource uri="file://Config/SpringDataAccess.xml" />
      <resource uri="file://Config/SpringVelocity.xml" />
    </context>
    <parsers>
      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
      <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />
      <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />
    </parsers>
</spring>

The XML configuration files were set to “Copy always” and had been copied into the application directory correctly.

Poking around in the Event Viewer I spotted an interesting error log message. Essentially it said “Exception: Error creating context 'spring.root': Could not find file 'C:\Windows\system32\Config\SpringContext.xml'.”

That was weird because the path (C:\Windows\system32) is not where I had put the application but clearly it was where the Windows service was being run from.

A quick solution was to reconfigure the application to use embedded resources for the configuration files:

<spring>
    <context>
      <resource uri="assembly://Assembly.Name.Here/Namespace.Here/Config.SpringContext.xml" />
      <resource uri="assembly://Assembly.Name.Here/Namespace.Here/Config.SpringDataAccess.xml" />
      <resource uri="assembly://Assembly.Name.Here/Namespace.Here/Config.SpringVelocity.xml" />
    </context>
    <parsers>
      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
      <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />
      <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />
    </parsers>
</spring>

The service now started correctly.