Skip to content

Instantly share code, notes, and snippets.

@RickStrahl
Last active February 25, 2020 07:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RickStrahl/e0740176f416640906c8bd5b3963265c to your computer and use it in GitHub Desktop.
Save RickStrahl/e0740176f416640906c8bd5b3963265c to your computer and use it in GitHub Desktop.
Masking IWebHostEnvironment in .NET Core 2.x for operation like 3.x

IWebHostingEnvironment is not available in .NET Core 2.x, but in 3.x it's the recommended way to access the host environment with threat of IHostingEnvironment being removed in the future. This can be a problem in libraries that need to run both on 2.x and 3.x.

The following tries to mask the differences in a multi-targeted .NET Core project by creating a custom IWebHostEnvironment implementations that picks up values from IHostingEnvironment in 2.x.

#if NETCORE2
using Microsoft.Extensions.FileProviders;

namespace Microsoft.AspNetCore.Hosting
{

    /// <summary>
    /// Implementation of a IWebHostEnvironment using locally faked
    /// IWebHostEnvironment and IHostEnvironment interfaces.
    ///
    /// This allows .NET Core 2.x to use IWebHostEnvironment without
    /// conditional code.
    ///
    /// This class simply forwards IHostingEnvironment properties
    /// into this class' properties.
    /// </summary>
    public class LegacyHostEnvironment : IWebHostEnvironment
    {
        public LegacyHostEnvironment(IHostingEnvironment environment)

        {
            ApplicationName = environment.ApplicationName;
            ContentRootFileProvider = environment.ContentRootFileProvider;
            ContentRootPath = environment.ContentRootPath;
            EnvironmentName = environment.EnvironmentName;
            WebRootFileProvider = environment.WebRootFileProvider;
            WebRootPath = environment.WebRootPath;
        }

        /// <summary>
        /// Name of the application - typically the package name (namespace)
        /// </summary>
        public string ApplicationName { get; set; }

        /// <summary>
        /// Content file provider that handles retrieving files from the content root
        /// </summary>
        public IFileProvider ContentRootFileProvider { get; set; }

        /// <summary>
        /// Path to the content root - folder where binaries live. Install folder.
        /// </summary>
        public string ContentRootPath { get; set; }

        /// <summary>
        /// Environment name: Production, Development, Staging etc.
        /// </summary>
        public string EnvironmentName { get; set; }

        /// <summary>
        /// File provider that retrieves files and folder information for the WebRoot path.
        /// </summary>
        public IFileProvider WebRootFileProvider { get; set; }

        /// <summary>
        /// Path to the Web root where Web Content lives. Typically the `wwwroot` folder, but it can be different if changed during startup.
        /// </summary>
        public string WebRootPath { get; set; }
    }
    
    /// <summary>
    /// Fake IWebHostEnvironment for .NET Core 2.x
    /// </summary>
    public interface IWebHostEnvironment : IHostEnvironment
    {
        /// <summary>
        /// Gets or sets an <see cref="T:Microsoft.Extensions.FileProviders.IFileProvider" /> pointing at <see cref="P:Microsoft.AspNetCore.Hosting.IWebHostEnvironment.WebRootPath" />.
        /// </summary>
        IFileProvider WebRootFileProvider { get; set; }

        /// <summary>
        /// Gets or sets the absolute path to the directory that contains the web-servable application content files.
        /// </summary>
        string WebRootPath { get; set; }
    }


    /// <summary>
    /// Fake IHostEnvironment for .NET Core 2.x
    /// </summary>
    public interface IHostEnvironment
    {
        /// <summary>
        /// Gets or sets the name of the application. This property is automatically set by the host to the assembly containing
        /// the application entry point.
        /// </summary>
        string ApplicationName { get; set; }

        /// <summary>
        /// Gets or sets an <see cref="T:Microsoft.Extensions.FileProviders.IFileProvider" /> pointing at <see cref="P:Microsoft.Extensions.Hosting.IHostEnvironment.ContentRootPath" />.
        /// </summary>
        IFileProvider ContentRootFileProvider { get; set; }

        /// <summary>
        /// Gets or sets the absolute path to the directory that contains the application content files.
        /// </summary>
        string ContentRootPath { get; set; }

        /// <summary>
        /// Gets or sets the name of the environment. The host automatically sets this property to the value of the
        /// of the "environment" key as specified in configuration.
        /// </summary>
        string EnvironmentName { get; set; }
    }

}
#endif

to use it you have a single point of config in startup.cs or middleware extension:

// in ConfigureServices or inside of middleware
#if NETCORE2
    var ihHost = app.ApplicationServices.GetService<IHostingEnvironment>();
    var host = new LegacyHostEnvironment(ihHost);
    services.AddSingleton<IWebHostEnvironment>(host);   
#endif

For this to work the library should multi target and add a flag that identifies .NET Core 2.x (NETCORE2):

<PropertyGroup>
    <TargetFrameworks>netcoreapp3.1;netcoreapp2.1;</TargetFrameworks>
    ...
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
    <DefineConstants>NETCORE2</DefineConstants>
</PropertyGroup>

With this in place you can now use IWebHostEnvironment in 2.x as you would in 3.x.

Am I making this too complicated? It works though and is easily reusable. Going into Westwind.AspnetCore package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment