lucacelenza
lucacelenza

Reputation: 1369

StructureMap injection per web request

I have an ASP.NET Web Api 2 REST service with several controllers. Each one of these controllers' constructor has got an ILogger argument which I have to inject with StructureMap. Is it possibile to instantiate an ILogger per request, in order to get client's specific informations - such as the IP address - and thus save the log file to a specific path?

This is the code I'm trying to run in the DefaultRegistry, but HttpContext is always null. Where am I wrong?

For<ILogger>()
    .Use(() => new TextFileLogger(
        HttpContext.Current.Request.UserHostAddress +
        DirectorySeparatorChar + "log.txt"));

Upvotes: 0

Views: 217

Answers (1)

Ric .Net
Ric .Net

Reputation: 5540

I'm not an expert on StructureMap but I understand the problem. The problem you have is that you use runtime data to build up your object graph. And injecting runtime data is an anti-pattern.

The simple solution here is not to use the HttpContext directly in your composition root but to create a provider which you can use to determine the path in the FileLogger. In that case both the FileLogger as this new provider can become singleton, which is far easier to work with and understand.

A provider could be as simple as (c#6 syntax):

public class HttpLogfileProvider : ILogFileProvider
{
     public string PathToLogfile => 
        Path.Combine(HttpContext.Current.Request.UserHostAddress, "log.txt");
}

Your FileLogger can use this provider as follows:

public class FileLogger : ILogger
{
    private readonly ILogFileProvider logFileProvider;
    public FileLogger(ILogFileProvider logFileProvider)
    {
        this.logFileProvider = logFileProvider;
    }

    public void Log(string message)
    {
         using (var writer = new StreamWriter(this.currentLogFile))
         {
              writer.WriteLine(message);
         }
    }

    private string currentLogFile =>
         this.logFileProvider.PathToLogFile;
}

I'm not an structuremap user but I think the registration should be:

For<ILogFileProvider>.Use<HttpLogfileProvider>.Singleton;
For<ILogger>.Use<FileLogger>.Singleton;

Upvotes: 4

Related Questions