asp.net-coreiis.net-9.0

How can I fix the Could not load type Microsoft.AspNetCore.Server.IIS.IIISEnvironmentFeature error when running a .NET 9.0 web app on Linux?


I have 4 .NET 9.0 web sites that I'm trying to get running on Linux; 3 of them run fine, but when I try to run the last one, I get the following error:

Could not load type 'Microsoft.AspNetCore.Server.IIS.IIISEnvironmentFeature' from assembly 'Microsoft.AspNetCore.Server.IIS, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'

It's happening on the host.Run() line.

public class Program
{
    public static void Main(string[] args)
    {
        var builder = CreateHostBuilder(args);

        var host = builder.Build();
        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

I can't find any reference to an IIS package or anything like that anywhere in the code. I've compared the program and startup files between the failing site and one that works but I'm not seeing anything obvious that's different between the two that should cause an IIS error. And my launchsettings profiles are not set to use IIS either.

Here's the launchsettings.json for the failing site.

{
  "profiles": {
    "FW": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "home",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
      },
      "dotnetRunMessages": "true",
      "applicationUrl": "https://localhost:7200;http://localhost:5200"
    }
  }
}

Here is the Startup.cs file:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
        SiteConfigurationSettings.LoadConfiguration(Configuration);
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        var mvcBuilder = services.AddControllersWithViews(options =>
            {
                // provides model binding for json that doesn't bind to a strongly-typed model
                options.ModelBinderProviders.Insert(0, new JsonValueTypeModelBinderProvider());
            })
            .AddSessionStateTempDataProvider()
            .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.PropertyNamingPolicy = null;
            });

        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders =
                ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        });

        services.Configure<KestrelServerOptions>(options =>
        {
            options.AllowSynchronousIO = true;
        });

        services.AddDataProtection()
            .PersistKeysToDbContext<DataProtectionDbContext>()
            .SetApplicationName("FW");

        // configures session to use SqlServer
        services.AddDistributedSqlServerCache(options =>
        {
            options.ConnectionString = DataSources.SessionStateSource;
            options.SchemaName = DataSources.SessionStateDefaultSchema;
            options.TableName = DataSources.FWSessionsTable;
        });

        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromHours(5);
            options.Cookie.Name = "FW.Session";
            options.Cookie.IsEssential = true;
        });

        services.Configure<RazorViewEngineOptions>(options =>
        {
            var expander = new CustomViewLocationExpander();
            options.ViewLocationExpanders.Add(expander);
        });

        services.Configure<FormOptions>(options =>
        {
            options.ValueCountLimit = int.MaxValue;
        });
        
        services.ConfigureOptions<StaticFilePathResolver>();
        services.AddRazorPages();

#if DEBUG
        mvcBuilder.AddRazorRuntimeCompilation();
#endif
        
         services.AddSystemWebAdapters();
         services.AddHttpLogging();

        services.AddHttpContextAccessor();
        services.AddDistributedSqlServerCache(x =>
        {
            x.ConnectionString = DataSources.CacheConnectionString;
            x.SchemaName = "dbo";
            x.TableName = "CacheData";
        });

        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        });
        
        var spec = new FWRegistrySpec();
        services.RegisterFHServices(spec);
        services.InitializeAutoMapper();
        services.AddMemoryCache();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.Use((context, next) =>
        {
            context.Response.Headers.Add("x-machine-name", Environment.MachineName);
            return next.Invoke();
        });

        app.UseMiddleware(typeof(FHHeadersMiddleware));
        
        app.UseHostFiltering();
        app.UseForwardedHeaders();
        app.UseHttpsRedirection();
        
        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(
                Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"))
        });
        app.UseSession();
        
        app.UseHttpLogging();

        app.Use(async (context, next) =>
        {
            context.Request.EnableBuffering();
            await next();
        });

        app.UseCookiePolicy(
            new CookiePolicyOptions
            {
                Secure = CookieSecurePolicy.Always
            });

        var startupManager = new PortalStartupManager(app);
        app = startupManager.RegisterErrorHandler();
        
        app.UseHsts();
        app.UseRouting();
        app.UseAuthorization();
        
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                "default",
                "{controller=Login}/{action=Index}/{id?}");

            endpoints.MapRazorPages();
        });

        app.UseSystemWebAdapters();
        startupManager.Startup();

        var scope = app.ApplicationServices.CreateScope();
        var security = scope.ServiceProvider.GetService<IFHWareSecurity>();
        security?.CreateSecurityCache();
    }
}

In one of the other sites that is working, the Program.cs file is identical and here is the Startup.cs file:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
        SiteConfigurationSettings.LoadConfiguration(Configuration);
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews(options =>
            {
                // provides model binding for json that doesn't bind to a strongly-typed model
                options.ModelBinderProviders.Insert(0, new JsonValueTypeModelBinderProvider());
            })
            .AddSessionStateTempDataProvider()
            .AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.PropertyNamingPolicy = null;
            });

        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders =
                ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        });

        services.Configure<KestrelServerOptions>(options =>
        {
            options.AllowSynchronousIO = true;
        });

        services.AddDataProtection()
            .PersistKeysToDbContext<DataProtectionDbContext>()
            .SetApplicationName("FH.Employees");

        // configures session to use SQL Server
        services.AddDistributedSqlServerCache(options =>
        {
            options.ConnectionString = DataSources.SessionStateSource;
            options.SchemaName = DataSources.SessionStateDefaultSchema;
            options.TableName = DataSources.EmployeesSessionTable;
        });

        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromHours(5);
            options.Cookie.Name = "FH.Employees.Session";
            options.Cookie.IsEssential = true;
        });

        // configures file paths for static web (js, css) assets
        services.ConfigureOptions<StaticFilePathResolver>();
        services.AddRazorPages();

        #if DEBUG
        mvcBuilder.AddRazorRuntimeCompilation();
        #endif

        services.AddHttpContextAccessor();
        services.AddDistributedSqlServerCache(x =>
        {
            x.ConnectionString = DataSources.CacheConnectionString;
            x.SchemaName = "dbo";
            x.TableName = "CacheData";
        });

        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        });

        var spec = new EmployeeRegistrySpec(services);
        services.RegisterFHServices(spec);
        services.InitializeAutoMapper();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseMiddleware(typeof(FHHeadersMiddleware));
        
        app.UseHostFiltering();
        
        app.UseForwardedHeaders();
        app.UseHttpsRedirection();

        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(
                Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"))
        });
        app.UseSession();

        app.Use(async (context, next) => {
            context.Request.EnableBuffering();
            await next();
        });
    
        app.UseCookiePolicy(
            new CookiePolicyOptions
            {
                Secure = CookieSecurePolicy.Always
            });
        
        var startupManager = new PortalStartupManager(app);
        app = startupManager.RegisterErrorHandler();

        app.UseHsts();

        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Login}/{action=Index}/{id?}");

            endpoints.MapRazorPages();
        });

        startupManager.Startup();
    }
}

And this is its launchsettings.json:

{
  "profiles":{
    "FH.Employees": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "dashboard",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
      },
      "dotnetRunMessages": "true",
      "applicationUrl": "https://localhost:44342;http://localhost:5000"
    }
  }
}

Update: as requested, here's the project file:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <LangVersion>13</LangVersion>
    <ImplicitUsings>true</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="EPPlus" Version="8.0.3" />
    <PackageReference Include="ExcelDataReader.DataSet" Version="3.6.0" />
    <PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.3" />
    <PackageReference Include="Microsoft.AspNetCore.SystemWebAdapters.CoreServices" Version="2.0.0" />
    <PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="9.0.3" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.3" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\EVerifyAPI\FH.EVerify.csproj" />
    <ProjectReference Include="..\FH.Infrastructure\FH.Infrastructure.csproj" />
    <ProjectReference Include="..\FH.Web.Shared\FH.Web.Shared.csproj" />
    <ProjectReference Include="..\FH\FH.BusinessLogic.Core.csproj" />
    <ProjectReference Include="..\FHObjectsStandard\FH.Core.csproj" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="Faker.Net.Standard.2.0">
      <HintPath>..\packages\Faker.Net.2.0.154\lib\netstandard2.0\Faker.Net.Standard.2.0.dll</HintPath>
    </Reference>
  </ItemGroup>

  <ItemGroup>
    <None Include="wwwroot\css\Activities\activities-collapsable-list.scss" />
    <None Include="wwwroot\scripts\sales\foxhire.pipeline.questionAttachment.js" />
  </ItemGroup>

  <ItemGroup>
    <AdditionalFiles Include="Views\Activity\Dashboard\Partials\ListView\_CollapsableActivityList.cshtml" />
    <AdditionalFiles Include="Views\Activity\Dashboard\Partials\ListView\_CollapsableActivityListItem.cshtml" />
  </ItemGroup>

</Project>

Solution

  • Copied from the comment

    This error occurs because UseSystemWebAdapters internally depends on Microsoft.AspNetCore.Server.IIS, which isn't supported on Linux.

    To fix it, either remove UseSystemWebAdapters or conditionally skip it on non-Windows platforms.