4

How can I restart a ServiceStack self hosted Apphost? Setting my AppHost instance to null and disposing of it does not work correctly, it throws the following Exception:

System.ArgumentException: An entry with the same key already exists.

I need to be able to do this to reload settings and start the AppHost without restarting the Windows Service hosting the AppHost

EDIT: Scott and Moo-Juice's suggestions to run the AppHost in a different AppDomain is the correct solution. In order to get past the Cross Domain calls to restart the AppHost, I created a second AppHost which runs in the Main AppDomain and calls the Restart method from Scott's solution. Enabling CORS on both AppHost instances allows for a simple $ajax call to restart the service and reload the page once the service is started and the request returns.

cornelha
  • 182
  • 1
  • 12
  • I host my service-stack instances in their own AppDomain which makes it trivial to start/stop/remove/add service-stack instances. – Moo-Juice Sep 25 '14 at 07:37
  • @Moo-Juice I actually haven't thought of doing that, thanks for the heads up – cornelha Sep 25 '14 at 07:46

1 Answers1

5

Use an AppDomain:

Moo-Juice's suggestion to use an AppDomain is correct. I have included a simple example of how to use an AppDomain to isolate ServiceStack, and allow it to be Started/Restarted/Stopped.

using System;
using ServiceStack;
using System.Runtime.Remoting;

namespace Test
{
    public class ServiceStackConsoleHost : MarshalByRefObject
    {
        public static void Main()
        {
            Start();
        }

        static ObjectHandle Handle;
        static AppDomain ServiceStackAppDomain;

        public static void Start()
        {
            // Get the assembly of our host
            var assemblyName = typeof(ServiceStackConsoleHost).Assembly.FullName;

            // Create an AppDomain
            ServiceStackAppDomain = AppDomain.CreateDomain("ServiceStackAppDomain");

            // Load in our service assembly
            ServiceStackAppDomain.Load(assemblyName);

            // Create instance of our ServiceStack application
            Handle = ServiceStackAppDomain.CreateInstance(assemblyName, "Test.ServiceStackConsoleHost");

            // Show that the main application is in a separate AppDomain
            Console.WriteLine("Main Application is running in AppDomain '{0}'", AppDomain.CurrentDomain.FriendlyName);

            // Wait for input
            Console.ReadLine();

            // Restart the application
            Restart();
        }

        public static void Stop()
        {
            if(ServiceStackAppDomain == null)
                return;

            // Notify ServiceStack that the AppDomain is going to be unloaded
            var host = (ServiceStackConsoleHost)Handle.Unwrap();
            host.Shutdown();

            // Shutdown the ServiceStack application
            AppDomain.Unload(ServiceStackAppDomain);

            ServiceStackAppDomain = null;
        }

        public static void Restart()
        {
            Stop();
            Console.WriteLine("Restarting ...");
            Start();

        }

        readonly AppHost appHost;

        public ServiceStackConsoleHost()
        {
            appHost = new AppHost();
            appHost.Init();
            appHost.Start("http://*:8090/");
            Console.WriteLine("ServiceStack is running in AppDomain '{0}'", AppDomain.CurrentDomain.FriendlyName);
        }

        public void Shutdown()
        {
            if(appHost != null)
            {
                Console.WriteLine("Shutting down ServiceStack host");
                if(appHost.HasStarted)
                    appHost.Stop();
                appHost.Dispose();
            }
        }
    }

    public class AppHost : AppSelfHostBase
    {
        public AppHost(): base("My ServiceStack Service", typeof(AppHost).Assembly)
        {
        }

        public override void Configure(Funq.Container container)
        {
        }
    }
}

Screenshot

Instructions for how to use an AppDomain can be found here.

Community
  • 1
  • 1
Scott
  • 21,211
  • 8
  • 65
  • 72
  • 1
    +1, my code is different but the philosophy is fundamentally the same :) – Moo-Juice Sep 25 '14 at 13:31
  • This does what I need it to, I built a small test project and it works very well. Now I just need to get around some reflection issues. Thanks a lot – cornelha Sep 25 '14 at 14:20
  • @cornelha No problem. Glad it was useful. Good luck with the reflection :) – Scott Sep 25 '14 at 14:21
  • @Scott I have implemented your suggestion, it works great when calling the method from within a Console Application, however when I call the method from within a ServiceStack Service class, I get the same exceptions as before regarding the port already being registered. Any ideas? – cornelha Oct 07 '14 at 13:35
  • @cornelha That's because if you call it from a ServiceStack service, then you are inside the ServiceStack app domain. And therefore you are not shutting it down. You would need your ServiceStack service to inform the console app that the service needs to shutdown/restart and have it terminate the instance, because it is outwith the AppDomain. It starts to get tricky there. – Scott Oct 07 '14 at 13:40
  • @Scott yeah, I have been doing some research on Cross AppDomain delegates, seems like there might be a solution there. Will post back here if I find something. Thank you. – cornelha Oct 07 '14 at 13:47
  • 1
    @Scott I found a solution which works nicely, thanks for your help – cornelha Oct 08 '14 at 19:20