How to add an existing object instance to Func?

I know that this is something one should usually NOT do but there are situations where it is required. Here is one I currently have:

I use Serilog as my logger. My services run in docker containers which may or maynot be orchestrated in a datacenter environment. I use a Graylog server to store log events from all my servers in a central place for easy monitoring and searching. The sysops in the datacenter need to be able to change the loglevel at runtime without restarting the services ideally from the command line in a bash shell using a script or curl.

My solution is a REST API which allows to change the log-level.

Serilog offers an object LoggingLevelSwitch to do that. In C# this looks as follows:

var logLevelSwitch = new LoggingLevelSwitch
{
	MinimumLevel = LogEventLevel.Information
};

var loggerConfig = new LoggerConfiguration()
	.MinimumLevel.ControlledBy(logLevelSwitch)   //<-- this allows dynamic changes later in the code
        .WriteTo.Graylog(new GraylogSinkOptions
		{
			HostnameOrAddress = po.LogHost,
			TransportType = TransportType.Udp,
			Port = 12201,
			Facility = "BizBus",
		});

Problem

I configure the logger in my Main() function in Program.cs long BEFORE the IOC container is initialized in AppHost.Configure(). This is why I would like to add the already existing instance of LoggingLevelSwitch to Func as soon as it is initialized, typically in the Main() function I would do:

var wHost = BuildWebHost(args, po);
var iocContainer = HostContext.Container;
// register instance of previously created LoggingLevelSwitch object something like
// iocContainer.Register(myLoggingLevelSwitch instance);
wHost.Run();

Is this somehow possible with the Func IOC container or do I need to create my own (properly serialized) singleton to achieve this?

The Container is initialized in the AppHost() constructor, so you can use the Container on the appHost instance:

var appHost = new AppHost();

appHost.Container.Register(instance);

//.. other stuff


// .NET Core
app.UseServiceStack(appHost);
// appHost.Init();  // Other AppHosts

var instance = HostContext.Container.Resolve<LoggingLevelSwitch>();

Only when the AppHost is initialized is it available from HostContext.Container.

Hi Demis,
This looks pretty smart, but unfortunately it crashes in my code because I don’t know how I can convert all my configuration parameters into the ServiceStack AppSettings object. Your lines

// .NET Core
app.UseServiceStack(appHost);

are a bit too short for me… !

I currently use ‘WebHostBuilder’ and a Startup class following this MS document. Since the Startup class has in my implementation only a Configure() method like so:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        Logger.Information("Entering Startup.Configure()...");
        try
        {
            //var appSettings = new NetCoreAppSettings(Configuration);
            app.UseServiceStack(new AppHost("MyService",  typeof(SomeService).Assembly)
            {
                AppSettings = new NetCoreAppSettings(Configuration)
            });
        }
        catch (Exception e)
        {
            Logger.Error(e.ToString());
            throw;
        }    
        app.Run(context =>
        {
            context.Response.Redirect("/metadata");
            return Task.FromResult(0);
        });
    }

I tried to get rid of this Startup() class and put it directly into the WebHostBuilder but I somehow have a black-out and don’t see how I can inject the configuration into the apphost instance! Here is my code in Program.cs:

static void Main(string[] args)
{
	//parse and verify commandline params
	// .....
	
	var appHost = new AppHost("MyService", typeof(SomeService).Assembly);
	var logLevelSwitch = new LoggingLevelSwitch
	{
		MinimumLevel = LogEventLevel.Information
	};
	appHost.Container.Register(logLevelSwitch);
	// setup and create logger depending on the command line params
	// ....

	var wHost = BuildWebHost(args, po, appHost);  <-- Add appHost instance to WebHostBuilder	
	wHost.Run();
}


private static IWebHost BuildWebHost(string[] args, ProgOptions progArgs, AppHost appHost)
{
	return new WebHostBuilder()
		.UseKestrel()
		.UseContentRoot(Directory.GetCurrentDirectory())
		.UseSerilog()
		.ConfigureAppConfiguration((builderContext, config) =>
		{
			//config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
			config.AddCommandLine(args);
			//config.AddEnvironmentVariables();	               
		})
		.UseDefaultServiceProvider((context, options) =>
		{
			options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); 
		})
		.Configure(app => app.UseServiceStack(appHost))  //<---- THIS IS THE PROBLEM!!  *)
		.UseUrls($"http://{progArgs.IpAddress}:{ServerPort.ToString()}")
		.Build();
}

*) This should correctly be

        .Configure(app => app.UseServiceStack(appHost)
        {
            AppSettings = new NetCoreAppSettings(Configuration)
        });

Where Configuration is the result of what is built in the lambda .ConfigureAppConfiguration((builderContext, config) => .... How do I get this Configuration collection??

It is crashing because I am referencing AppSettings in the AppHost.Configure() method all the time and with the code above it is not assigned…

How can I get all my configuration settings (command line params and in some situations also EnvironmentVariables) into the ServiceStack AppSettings??

Instead of all this gymnastics of trying to preserve an AppHost instance, just save a static instance of your dependency you want to register:

AppHost.LoggerConfig = ...;

// Then register it in your AppHost.Configure()
public override void Configure(Container container)
{
    container.Register(LoggerConfig);
}

Otherwise if it’s easier you can register it in .NET Core’s IOC In ConfigureServices() as ServiceStack will also be able to resolve it.

Yeah, more of a coding marathon then gymnastics, I use static and it just works fine with two lines… Thanks again!

1 Like