0

As previously discussed here, I am sometimes using this approach to execute Services internally:

IMessage theMessage = new Message<Startup>(new Startup());
var reply = HostContext.AppHost.ExecuteMessage(theMessage); // The way to get the pipeline to execute when we do internal call, and get DI to work etc

This works well and as expected, except in one case: when I call this from the OnAfterInit method, that I override.

The idea is that I want to do some startup-stuff when the AppHost is ready, after initialization, but when doing this, it fails with a NullReferenceException:

public override void OnAfterInit()
{
    base.OnAfterInit();

    IMessage theMessage = new Message<Startup>(new Startup());
    var reply = HostContext.AppHost.ExecuteMessage(theMessage); 
}

Error:

enter image description here

   at ServiceStack.ServiceStackHost.<ApplyRequestConvertersAsync>d__361.MoveNext() in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\ServiceStackHost.Runtime.cs:line 45
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at ServiceStack.Host.ServiceController.ExecuteMessage(IMessage dto, IRequest req) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\ServiceController.cs:line 584
   at ServiceStack.ServiceStackHost.ExecuteMessage(IMessage mqMessage) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\ServiceStackHost.cs:line 1486
   at Test.Test.OnAfterInit() in C:\Git\Test\Test.cs:line 59

It seems that not all is initialized and ready. If I do this, it works:

public override void OnAfterInit()
{
    base.OnAfterInit();
    System.Threading.Timer t = new System.Threading.Timer((o) => 
    {
        IMessage theMessage = new Message<Startup>(new Startup());
        var reply = HostContext.AppHost.ExecuteMessage(theMessage);
    }, null, 100, int.MaxValue);
}

I would appreciate to know if I am missing something, and if there is a nicer way to resolve this. The Timer-thing works, but it doesn't sit right with me.

Ted
  • 19,727
  • 35
  • 96
  • 154

1 Answers1

0

Executing Services should ideally be done at runtime not on Startup but if you do want to Execute Services you'll need to execute them at the very end of Startup when the rest of ServiceStack has initialized like in an AfterInitCallbacks, e.g:

AfterInitCallbacks.Add(host => 
    host.ExecuteMessage(new Message<Startup>(new Startup()));
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Thanks, that seems to work! Startup is my own custom class, its just something I want to be executed after the rest of Servicestack has been initialized, and I thought that the OnAfterInit was just that. – Ted Dec 06 '20 at 14:59
  • @Ted I wouldn't put setup logic in a Runtime service. Have a look at [Modular Startup](https://docs.servicestack.net/modular-startup), e.g. you can execute your own startup logic after the AppHost has initialized by having a class in your [AppHost implement `IAfterInitAppHost`](https://docs.servicestack.net/modular-startup#modular-startup-prioritization). – mythz Dec 06 '20 at 18:17
  • I might not have the same baseline vocabulary, but "Runtime service" isn't clear to me, so to clarify: 1) My `Startup` class isnt the standard `Startup` in .NET Core, I just happened to name it the same way. 2) The above code is something I wrote in the descendant to the AppHost class, do you consider that a "Runtime service"? Other startup-related things are in the AppHost/descendandt, like Configure etc, so I thought it'd be natural to have other startup-related code there? 3) I will try t get a grip on ModularStartup. Thanks! – Ted Dec 06 '20 at 19:33
  • 1
    Quite simply **all Services** should only be called at runtime, i.e. no Services are meant to be called at Startup. The purpose of the Startup class is to initialize your App & it's dependencies which is done in a single main thread, once Startup has completed the server spawns multiple worker threads to execute incoming requests, that is what I mean by Services executed at runtime - they're executed by a Servers worker thread after the App has initialized. So if the purpose of this Service is to exec Startup logic, it shouldn't be in a Service, it should be apart of the Startup process. – mythz Dec 07 '20 at 02:59
  • I tried following the info on that page, but I can't really figure out what to do, since I do not have a "standard Startup class"; I inherit directly from AppHostBase, so `public abstract class MyBaseClass : AppHostBase`, and then other classes inherit `MyBaseClass`, until I get to my "leaf module". It is in that leaf module/class I overrode `OnAfterInit`. Do you mean I should add the IAfterInitAppHost to `MyBaseClass`, like `public abstract class MyBaseClass : AppHostBase, IAfterInitAppHost`? – Ted Dec 07 '20 at 08:42
  • @Ted If you're using .NET Core you should have a `Startup` class if you're not you wont. If you don't want the logic in your AppHost I'd put your Startup logic in a [custom Plugin](https://docs.servicestack.net/plugins) and either [have it implement IAfterInitAppHost](https://docs.servicestack.net/plugins#custom-logic-after-apphost-is-initialized) or register an `AfterInitCallbacks` containing your custom logic. – mythz Dec 07 '20 at 09:06