Search code examples
.netmsdeployvisual-studio-2022c#-10.0

Publishing a .NET 6 project with C# 10 implicit usings via Visual Studio 2022


When publishing an ASP.NET Core 6 web application with C# 10's <ImplicitUsings /> enabled to an Azure App Service from a Visual Studio 2022 publishing profile, the publishing build fails due to missing using statements.

Background

C# 10 introduces the new implicit usings feature, where certain using directives are treated as global using directives based on the SDK. This can be enabled with the following csproj configuration:

<PropertyGroup>
    <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

This works exactly as expected with a local build. E.g., when targeting the Microsoft.NET.Sdk.Web SDK, I'm able to remove using directives for System, System.Collections.Generic, System.Linq, &c.

Publishing Error

When publishing to an Azure App Service from a Visual Studio 2022 publishing profile, however, the build output shows errors such as the following:

C:\Code\MyWebApp\MyClass.cs(41,25): Error CS0246: The type or namespace name 'IEnumerable<>' could not be found (are you missing a using directive or an assembly reference?)

This can be seen below as part of an expanded context:

Build started...
1>------ Build started: Project: MyWebApp, Configuration: Release Any CPU ------
1>Done building project "MyWebApp.csproj".
2>------ Publish started: Project: MyWebApp, Configuration: Release Any CPU ------
Determining projects to restore...
All projects are up-to-date for restore.
C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\Roslyn\csc.exe /noconfig /unsafe- /checked- /nowarn:1701,1702,1701,1702,2008 /fullpaths /nostdlib+ /platform:x64 /errorreport:prompt /warn:6 /define:TRACE;RELEASE;NET;NET6_0;NETCOREAPP;NET5_0_OR_GREATER;NET6_0_OR_GREATER;NETCOREAPP1_0_OR_GREATER;NETCOREAPP1_1_OR_GREATER;NETCOREAPP2_0_OR_GREATER;NETCOREAPP2_1_OR_GREATER;NETCOREAPP2_2_OR_GREATER;NETCOREAPP3_0_OR_GREATER;NETCOREAPP3_1_OR_GREATER /errorendlocation /preferreduilang:en-US /highentropyva+ /reference: /debug+ /debug:portable /filealign:512 /optimize+ /out:obj\Release\net6.0\win-x64\MyWebApp.dll /refout:obj\Release\net6.0\win-x64\ref\MyWebApp.dll /target:exe /warnaserror- /utf8output /deterministic+ /langversion:10.0 /analyzerconfig:… /analyzer:"C:\Program Files\dotnet\sdk\6.0.100\Sdks\Microsoft.NET.Sdk.Web\analyzers\cs\Microsoft.AspNetCore.Analyzers.dll" /additionalfile:Areas\MyArea\Index.cshtml … /warnaserror+:NU1605
C:\Code\MyWebApp\MyClass.cs(41,25): Error CS0246: The type or namespace name 'IEnumerable<>' could not be found (are you missing a using directive or an assembly reference?)
2>Build failed. Check the Output window for more details.
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
========== Publish: 0 succeeded, 1 failed, 0 skipped ==========

I assume this has something to do with the csc.exe command generated by the publishing profile.

Publishing Profile

Unfortunately, it's not immediately obvious to me how to remedy this within my pubxml file, which is pretty straight-forward:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <ResourceId>/subscriptions/77e95f68-ed69-4bfe-9bbe-0b4d3910722e/resourceGroups/ResourceGroup/providers/Microsoft.Web/sites/MyWebApp</ResourceId>
    <PublishProvider>AzureWebSite</PublishProvider>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <ProjectGuid>77e95f68-ed69-4bfe-9bbe-0b4d3910722e</ProjectGuid>
    <MSDeployServiceURL>mywebapp.scm.azurewebsites.net:443</MSDeployServiceURL>
    <DeployIisAppPath>MyWebApp</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <EnableMSDeployBackup>True</EnableMSDeployBackup>
    <UserName>$MyWebApp</UserName>
    <_SavePWD>True</_SavePWD>
    <_DestinationType>AzureWebSite</_DestinationType>
    <TargetFramework>net6.0</TargetFramework>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <SelfContained>false</SelfContained>
    <InstallAspNetCoreSiteExtension>False</InstallAspNetCoreSiteExtension>
    <EnableMsDeployAppOffline>True</EnableMsDeployAppOffline>
  </PropertyGroup>
</Project>

(Note: ResourceId, ProjectGuid, &c. have been anonymized and don't refer to an actual property.)

Question

Acknowledging this is a new feature in a new version of Visual Studio, this could be a bug. That said, are there any pubxml properties that are needed to enable the <ImplicitUsing /> feature? Or is there something else that's needed to enable this feature with Visual Studio publishing profiles?


Solution

  • First of all, it appears that this was a false alarm. I apparently neglected to finish rebooting my workstation after installing the release version of Visual Studio 2022. After restarting, I am now able to publish with full support for implicit usings. Whoops.

    That said, this does provide a good opportunity to offer some insight into what I discovered after the fact, which may help future readers with similar types of issues—and certainly helped me better understand the integration between the various command-line tools.

    Command-Line Arguments

    Notably, there doesn't appear to be any command-line parameters for handling implicit usings built into any of the following tools:

    • .NET 6.0 SDK (dotnet.exe)
    • Visual C# compiler (csc.exe)
    • Microsoft Build Engine (msbuild.exe)

    Microsoft Build Support

    Instead, this is handled via the Microsoft Build Engine (msbuild.exe) when working off of a C# project file (*.csproj), at which point it generates the following intermediate file at:

    /{BaseIntermediateOutputPath}/{Configuration}/net6.0/{MyWebApp}.GlobalUsings.g.cs
    

    e.g., by default:

    /obj/Release/net6.0/MyWebApp.GlobalUsings.g.cs
    

    This file contains global using directives that are specific to the configured SDK:

    // <auto-generated/>
    global using global::Microsoft.AspNetCore.Builder;
    global using global::Microsoft.AspNetCore.Hosting;
    …
    

    In turn, this file is appended to the underlying call to the C# Compiler (csc.exe).

    Note: The .NET 6.0 SDK—i.e., dotnet.exe—provides a wrapper around msbuild.exe as part of either the build or publish commands, and thus offers the same functionality. Further, by default, the .NET SDK will persist the intermediate files in the /obj folder, whereas msbuild.exe immediately deletes them.

    C# 10 Support

    Given this, the implicit usings feature isn’t actually part of C# 10 per sedespite being advertised as such—but is rather a bit of tooling built into the Microsoft Build Engine (msbuild.exe). The C# 10 compiler (csc.exe) does supports global using directives, but has no awareness of implicit usings.

    Ultimately, this isn't too surprising, as the C# compiler (csc.exe) isn't actually aware of C# project files (*.csproj) themselves—those are an artifact of the Microsoft Build Engine, with pass-through support via the .NET SDK.

    Conclusion

    It's unclear why local builds worked successfully with this prior to a full reboot, while calls using Visual Studio 2022's publishing profiles failed. I assume this has something to do with how Visual Studio assembles the arguments for the C# Compiler (csc.exe) as part of the publishing process. Regardless, it's an easy solution to just reboot the workstation after installation. Otherwise, this post will hopefully help demystify the overall process.