wpf: Visual Studio 17.2.6 Breaks CommunityToolkit.Mvvm

Version Used: 17.2.6

Steps to Reproduce:

  1. Create a WPF exe project targeting net6.0-windows
  2. Reference CommunityToolkit.Mvvm like so: <PackageReference Include="CommunityToolkit.Mvvm" Version="7.1.2" />
  3. Create a simple class inheriting from ObservableObject, like so:
using CommunityToolkit.Mvvm.ComponentModel;

namespace ObservablePropertyTest
{
    public partial class TestVM : ObservableObject
    {
        [ObservableProperty]
        private string _testString = string.Empty;
    }
}

Expected Behavior: In 17.2.5, it built and ran fine.

Actual Behavior:

Rebuild started...
1>------ Rebuild All started: Project: ObservablePropertyTest, Configuration: Debug Any CPU ------
Restored C:\git\ObservablePropertyTest\ObservablePropertyTest.csproj (in 2 ms).
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\__KnownINotifyPropertyChangedOrChangingArgs.cs(12,27,12,70): error CS0101: The namespace 'CommunityToolkit.Mvvm.ComponentModel.__Internals' already contains a definition for '__KnownINotifyPropertyChangedOrChangingArgs'
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\__KnownINotifyPropertyChangedOrChangingArgs.cs(7,6,7,51): error CS0579: Duplicate 'global::System.CodeDom.Compiler.GeneratedCode' attribute
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\__KnownINotifyPropertyChangedOrChangingArgs.cs(8,6,8,52): error CS0579: Duplicate 'global::System.Diagnostics.DebuggerNonUserCode' attribute
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\__KnownINotifyPropertyChangedOrChangingArgs.cs(9,6,9,69): error CS0579: Duplicate 'global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage' attribute
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\__KnownINotifyPropertyChangedOrChangingArgs.cs(10,6,10,51): error CS0579: Duplicate 'global::System.ComponentModel.EditorBrowsable' attribute
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\__KnownINotifyPropertyChangedOrChangingArgs.cs(11,6,11,29): error CS0579: Duplicate 'global::System.Obsolete' attribute
1>C:\git\ObservablePropertyTest\CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator\ObservablePropertyTest.TestVM.cs(12,23,12,33): error CS0102: The type 'TestVM' already contains a definition for 'TestString'
1>Done building project "ObservablePropertyTest_yynlzhol_wpftmp.csproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

It appears that somehow the CommunityToolkit.Mvvm source generators are being run twice.

If you create a WPF class library, things work fine. They’re only broken in a WPF app.

Here is my test project: ObservablePropertyTest.zip

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 19
  • Comments: 57 (9 by maintainers)

Commits related to this issue

Most upvoted comments

For a workaround @ThomasGoulet73 I tend to prefer @jlaanstra’s from https://github.com/dotnet/wpf/pull/6680#issuecomment-1183552170 with a small modification to use MSBuild engine functionality instead of a task:

<Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile">
    <!-- Work around https://github.com/dotnet/wpf/issues/6792 -->

    <ItemGroup>
        <FilteredAnalyzer Include="@(Analyzer->Distinct())" />
        <Analyzer Remove="@(Analyzer)" />
        <Analyzer Include="@(FilteredAnalyzer)" />
    </ItemGroup>
</Target>

You still can use the latest version of Visual Studio. Here is a workaround that works for me:

  1. Download and install the previous version of .NET SDK (6.0.301) https://dotnet.microsoft.com/en-us/download/dotnet/6.0
  2. Add global.json file to the root of your solution (where .sln file is located)
  3. The content of the global.json:
{
    "sdk": {
        "version": "6.0.301",
        "rollForward": "disable"
    }
}
  1. Rebuild solution.

I apologize for the regression. It was caused by my changes in dotnet/wpf#6534 which were backported to .Net 6 in dotnet/wpf#6680.

I posted workaround for this issue here: https://github.com/dotnet/wpf/pull/6680#issuecomment-1183455941. This is an alternative to this workaround: https://github.com/dotnet/wpf/issues/6792.

I’m working on a fix that can hopefully be included in .Net 6.0.8.

This issue should now be fixed in .Net SDK 6.0.9 released today.

For a workaround @ThomasGoulet73 I tend to prefer @jlaanstra’s from #6680 (comment) with a small modification to use MSBuild engine functionality instead of a task:

<Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile">
    <!-- Work around https://github.com/dotnet/wpf/issues/6792 -->

    <ItemGroup>
        <FilteredAnalyzer Include="@(Analyzer->Distinct())" />
        <Analyzer Remove="@(Analyzer)" />
        <Analyzer Include="@(FilteredAnalyzer)" />
    </ItemGroup>
</Target>

That worked for me !!!

The fix was backported to .Net 6 but it was not shipped with .Net 6.0.8. It should be included in .Net 6.0.9 according to the milestone for the PR responsible for backporting to .Net 6 (dotnet/wpf#6800).

Reopening till the fix gets into .NET 6

You still can use the latest version of Visual Studio. Here is a workaround that works for me:

  1. Download and install the previous version of .NET SDK (6.0.301) https://dotnet.microsoft.com/en-us/download/dotnet/6.0
  2. Add global.json file to the root of your solution (where .sln file is located)
  3. The content of the global.json:
{
    "sdk": {
        "version": "6.0.301",
        "rollForward": "disable"
    }
}
  1. Rebuild solution.

Cannot Install .NET SDK (6.0.301) as it is showing error - Another version of this product is already installed

@saivineeth100 , I had the same problem. You may have version 6.0.302 installed. I uninstalled 6.0.302 using the control panel. After that I was able to install 6.0.301.

@ecxdev If you have .NET 7 preview-7 upgrade to .NET 7 RC-1 and it should fix it. Either that or specify version 6 in global.json

Pointing out, that Winodws Update (at least on W11) may automatically update your SDK to broken version.

I installed SDK 6.0.301 (.NET 6.0.6) yesterday and everything worked for me. Today, the build failed again with duplication of analyzers error.

Turns out, Windows Update updated .NET SDK to version 6.0.303 (.NET 6.0.7) as part of 2022-08 .NET 6.0.8 Security Update for x64 Client (KB5016990) update.

Tested it manually:

  1. uninstalled SDK 6.0.303
  2. installed SDK 6.0.301
  3. listed SDKs
  4. ran Windows Update
  5. listed SDKs again.

list-sdk

I can also confirm that with the latest update VS 17.3.0 and SDK 6.0.400, the duplication of the analyzers is still present.

It seems that the fix wasn’t backported - https://github.com/dotnet/wpf/blob/v6.0.8/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft.WinFX.targets.

Why?

//cc @dipeshmsft

Install .net 6.0.301 SDK

Its fixed for me in VS 17.3 and Toolkit 8

Interesting. I just tried VS 2022 17.3 including .NET SDK 6.0.400 and the issue is still not fixed for me. The “duplicate PreserveAttribute” compile error still occurs on WPF projects that are using Refit (6.3.2).

Sure, the workaround of installing .NET SDK 6.0.301 and pinning the version down via global.json is still working. But the SourceGenerator issue that was introduced in 6.0.302 is still not fixed.

you may need to uninstall some SDK using dotnet-core-uninstall Tool https://docs.microsoft.com/en-us/dotnet/core/additional-tools/uninstall-tool?tabs=windows

It’s in the Csc task which contains the build errors, under CommandLineArguments

image

In this case the paths appear to be:

  • C:\Users\jamie.hankins\.nuget\packages\communitytoolkit.mvvm\7.1.2\analyzers\dotnet\cs\CommunityToolkit.Mvvm.SourceGenerators.dll
  • C:\Users\jamie.hankins\.nuget\packages\communitytoolkit.mvvm\7.1.2\analyzers\dotnet\cs\CommunityToolkit.Mvvm.SourceGenerators.dll

In other words, the two paths are exactly the same. It’s odd that msbuild would do that, It feels like the compiler should be modified to give a diagnostic and/or dedupe the duplicate generators. I’ll have to look more closely later to confirm that and determine a root cause.

Hey @Gakk, I doubt that your issue is related to analyzers/generators.

This is likely the cause: I experienced this issue with the generators when switching from legacy project file to SDK style project file

Without a repro I can only speculate but it looks like you use your own Main entry point instead of the generated App.Main and pass a SplashScreen to the constructor of App. In your legacy project file, App.xaml was probably included as Page instead of ApplicationDefinition which meant that App.Main was never generated. If you do not use the generated App.Main, you need to set the MSBuild property EnableDefaultApplicationDefinition to false in your project. If this does not fix your issue, please open a new Github issue without a repro project since it is probably unrelated to this one.

Thanks.

Install .net 6.0.301 SDK

Its fixed for me in VS 17.3 and Toolkit 8

Interesting. I just tried VS 2022 17.3 including .NET SDK 6.0.400 and the issue is still not fixed for me. The “duplicate PreserveAttribute” compile error still occurs on WPF projects that are using Refit (6.3.2).

I had weird issues like ‘void’ and ‘string’ being undefined. Fixed it using this comment; #6792 (comment)

That’s intellisense trying to find the code analysers in the wrong SDK directory.

Either:

  • throw away your obj directories (actually a good idea on any VS update) or
  • manually open the properties of each problematic project [alt-enter], which triggers an automatic update of the paths.

I had weird issues like ‘void’ and ‘string’ being undefined. Fixed it using this comment; https://github.com/dotnet/wpf/issues/6792#issuecomment-1183530305

(I typed up most of this before @thomasGoulet73 replied with an authoritative answer, but am going to post it since it shows my debugging process)

Here’s the output from building my tiny test project in case you have a better idea of where in there to look. msbuild.zip

It’s in the Csc task which contains the build errors, under CommandLineArguments

image

In this case the paths appear to be:

  • C:\Users\jamie.hankins\.nuget\packages\communitytoolkit.mvvm\7.1.2\analyzers\dotnet\cs\CommunityToolkit.Mvvm.SourceGenerators.dll
  • C:\Users\jamie.hankins\.nuget\packages\communitytoolkit.mvvm\7.1.2\analyzers\dotnet\cs\CommunityToolkit.Mvvm.SourceGenerators.dll

Looking at this a bit more closely, looks like one of them has IsImplicitlyDefined=true metadata.

Searching through Find in Files for Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll, it looks like that’s coming from this blob of C:\Program Files\dotnet\sdk\6.0.302\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.Analyzers.targets

  <!-- .NET Analyzers -->
  <ItemGroup Condition="$(EnableNETAnalyzers)">
    <Analyzer
      Condition="'$(Language)' == 'VB'"
      Include="$(MSBuildThisFileDirectory)..\analyzers\Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers.dll"
      IsImplicitlyDefined="true" />
    <Analyzer
      Condition="'$(Language)' == 'C#'"
      Include="$(MSBuildThisFileDirectory)..\analyzers\Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll"
      IsImplicitlyDefined="true" />
    <Analyzer
      Include="$(MSBuildThisFileDirectory)..\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll"
      IsImplicitlyDefined="true" />
  </ItemGroup>

And that’s the only copy I see explicitly mentioned in MSBuild XML.

Looking through the Search Log tab for Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll, I can see that it’s passed to the compilers (expected), and also handled in _HandlePackageFileConflicts, an SDK task that is supposed to handle the case where you have (for instance) the same reference applied via a NuGet package and via the targeted runtime, which should be deduplicating.

At the input to that task it’s not duplicated:

image

It’s also not duplicated on the output side of that task:

image

Ah! All of that is in the main build of the project, but the actual failure is in c:\git\ObservablePropertyTest\ObservablePropertyTest_kb232yxo_wpftmp.csproj, which is a (horrible) implementation detail of XAML builds: the project is copied with modifications and the copy is built within the normal build.

The copy has all of its references and analyzers flattened, so its build doesn’t have to waste time resolving them. So it’s getting the analyzer from this line (note that there’s no metadata on this one):

    <Analyzer Include="C:\Program Files\dotnet\sdk\6.0.302\Sdks\Microsoft.NET.Sdk\targets\..\analyzers\Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll" />

But it’s also getting added by the Microsoft.NET.Sdk.Analyzers.targets logic, which is the bug.

This is a generic problem that affects all source generator projects. It seems to occur only when referenced by the nuget package, If referenced in the following (manual reference), there will be no problem, so the test cases within the source generator project are normal.

    <ProjectReference Include="..\Xxx.Generator\Xxx.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />