16

How do I use the IHostingEnvironment interface without initiating it in a constructor?

my Startup.cs:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();

    if (env.IsDevelopment())
    {
        // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
        builder.AddApplicationInsightsSettings(developerMode: true);
    }
    Configuration = builder.Build();
}

My Class:

public class FileReader
{
    // I don't want to initiate within a constructor

    public string ReadFile(string fileName)
    {
       // this is wrong! how can I use it?
       var filename = Path.Combine(IHostingEnvironment.WebRootPath, fileName); 
    }
}
CDspace
  • 2,639
  • 18
  • 30
  • 36
Eyal
  • 4,653
  • 9
  • 40
  • 56

1 Answers1

19

You should use the integrated dependency injection as it makes your code decoupled and easier to change without breaking stuff and make it very easy to unit test.

If you would access it via static class or method, then your code becomes hard to test as in unit test it would always use the project path and not some specific string for testing.

The way you described it absolutely correct and it's NOT wrong! Below just the complete example.

public class FileReader
{
    private readonly IHostingEnvironment env;
    public FileReader(IHostingEnvironment env)
    {
        if(env==null)
            throw new ArgumentNullException(nameof(env));

        this.env = env;
    }

    public string ReadFile(string fileName)
    {
       var filename = Path.Combine(env.WebRootPath, fileName);
    }
}

If the env value is null in the constructor, you may need to register it yourself in Startup.cs, if ASP.NET Core doesn't do it already:

services.AddSingleton<IHostingEnvironment>(env);

where env is the instance passed to the Startup constructor.

Tseng
  • 61,549
  • 15
  • 193
  • 205
  • 1
    if I am use 'IHostingEnvironment' in the constructor, how can I initiate the class FileReader? var fileReader = new FileReader(); It expect 'IHostingEnvironment' as a parameter. – Eyal Dec 03 '16 at 14:42
  • 2
    You don't instantiate it, you request it via constructor. When you use DI/IoC you should use it **everywhere**. So if you need it in the controller your controller constructor may look something like `public MyController(FileReader fileReader)`, then you keep a reference to it and access it in the action. When you use DI you never call `new` on your services (just models and certain framework classes not controlled/resolved via DI) – Tseng Dec 03 '16 at 14:52
  • 1
    But you should split it into an interface, having `IFileReader` and `FileReader` and only inject the interface. Then when writing unit tests, you can easily mock the interface, which isn't possible with `FileReader` – Tseng Dec 03 '16 at 14:54