NuGet / Home

Repo for NuGet Client issues
Other
1.5k stars 252 forks source link

[Bug]: Cannot get INuGetProjectService from the service broker in Visual Studio 17 #11367

Closed reduckted closed 2 years ago

reduckted commented 3 years ago

NuGet Product Used

MSBuild.exe

Product Version

Visual Studio 17.0.0

Worked before?

No response

Impact

I'm unable to use this version

Repro Steps & Context

An exception is thrown in Visual Studio 2017 2022 when trying to get an INuGetProjectService from the Visual Studio service broker. This code:

var serviceBrokerContainer = await ServiceProvider.GetGlobalServiceAsync<SVsBrokeredServiceContainer, IBrokeredServiceContainer>();
var serviceBroker = serviceBrokerContainer.GetFullAccessServiceBroker();

// This next line throws the exception.
var projectService = await serviceBroker.GetProxyAsync<INuGetProjectService>(NuGetServices.NuGetProjectServiceV1);

Throws this error in Visual Studio 17.0.0:

Activating the "Microsoft.VisualStudio.NuGet.NuGetProjectService (1.0)" service failed. Unable to cast object of type 'NuGet.VisualStudio.Implementation.Extensibility.NuGetProjectService' to type 'NuGet.VisualStudio.Contracts.INuGetProjectService'.

The package references I'm using are:

<PackageReference Include="Microsoft.VisualStudio.SDK">
  <Version>17.0.31902.203</Version>
</PackageReference>
<PackageReference Include="NuGet.Protocol">
  <Version>6.0.0</Version>
</PackageReference>
<PackageReference Include="NuGet.VisualStudio">
  <Version>6.0.0</Version>
</PackageReference>
<PackageReference Include="NuGet.VisualStudio.Contracts">
  <Version>6.0.0</Version>
</PackageReference>

I'm also seeing a warning about the version of Microsoft.ServiceHub.Framework not being found. Could this be related? It seems like the NuGetProjectService returned from the service broker is implementing the INuGetProjectService interface from a different assembly to the one I am expecting.

Using this project file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="NuGet.VisualStudio.Contracts" Version="6.0.0" />
  </ItemGroup>
</Project>

Restoring packages gives this warning:

NU1603 NuGet.VisualStudio.Contracts 6.0.0 depends on Microsoft.ServiceHub.Framework (>= 2.7.327-preview) but Microsoft.ServiceHub.Framework 2.7.327-preview was not found. An approximate best match of Microsoft.ServiceHub.Framework 2.7.339 was resolved.

Microsoft.ServiceHub.Framework 2.7.327-preview is not listed on NuGet.

Verbose Logs

No response

zivkan commented 3 years ago

NuGet follows Visual Studio's version, minus 11.0. Therefore NuGet.VisualStudio.Contracts package version 6.0 tracks Visual Studio 6.0 + 11.0 = 17.0. Note this is dev17, not Visual Studio 2017, which is dev15.

If you're targeting Visual Studio 2017, which corresponds to NuGet 4.x, you should that version of the NuGet.VisualStudio package. NuGet.VisualStudio.Contracts was added in dev16 (VS 2019), so it's expected not to work in dev15.

The only API that INuGetProjectService currently has is GetInstalledPackagesAsync. Earlier versions of VS only supports our non-async (blocking) IVsPackageInstallerServices.GetInstalledPackages

reduckted commented 3 years ago

🤦‍♂️ Sorry, I got my version numbers confused. I meant Visual Studio 2022 (v17.0.0).

@zivkan can you reopen this, please?

zivkan commented 3 years ago

@reduckted we have a tool we use to test our VS APIs, available here: https://github.com/NuGet/Entropy/tree/main/IVsTestingExtension

Can you try it out and see if you still have the problem? It works for me, I was using it today in fact.

My best guess is that your vsix contains your own copy of NuGet.VisualStudio.Contracts.dll, and so VS is loading both copies of the dll. Even when two assemblies are the same assembly version, a type created in path1\some.dll can't be typecast to the same type in path2\some.dll. NuGet tells VS to create binding redirects though, and we don't have a problem with this testing extension, so I don't have any ideas what the issue could be.

reduckted commented 3 years ago

My best guess is that your vsix contains your own copy of NuGet.VisualStudio.Contracts.dll

You were correct! The NuGet assemblies were being included in the VSIX. However, when I exclude the NuGet files from the VSIX, I get a different error:

System.IO.FileNotFoundException: 'Could not load file or assembly 'NuGet.VisualStudio.Contracts, Version=6.0.0.280, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'

I tried the Entropy extension and it worked from the latest commit (488ac98). But that version is using 5.10.0 of the NuGet packages. If I update those two packages (NuGet.VisualStudio and NuGet.VisualStudio.Contracts) to version 6.0.0, then I get the original error about being unable to cast the service to the required type.

Could the binding redirects not be including the latest version of the NuGet packages? The only binding redirects for NuGet assemblies that I could find in the Visual Studio installation directory were in vsn.exe.config, and they list this:

<!-- NuGet -->
<dependentAssembly>
  <assemblyIdentity name="NuGet.VisualStudio" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
  <bindingRedirect oldVersion="0.0.0.0-6.0.0.275" newVersion="6.0.0.275"/>
  <codeBase version="6.0.0.275" href="CommonExtensions\Microsoft\NuGet\NuGet.VisualStudio.dll"/>
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name="NuGet.VisualStudio.Contracts" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
  <bindingRedirect oldVersion="0.0.0.0-6.0.0.275" newVersion="6.0.0.275"/>
  <codeBase version="6.0.0.275" href="CommonExtensions\Microsoft\NuGet\NuGet.VisualStudio.Contracts.dll"/>
</dependentAssembly>

That has an upper bound of 6.0.0.275, but the version in the NuGet.VisualStudio.Contracts.6.0.0 package is 6.0.0.280.

zivkan commented 3 years ago

Yes, you're right. We screwed up. NuGet inserts into both Visual Studio and the .NET SDK, but NuGet has a mono-repo with both our VS and .NET SDK assets being created from the same build. Because reasons, we had to insert a higher build number of NuGet into the .NET SDK than we did into VS, which is normally the other way around. The packages we published to nuget.org was the highest of the two, but this breaks VS extension developers using NuGet.VisualStudio.Contracts.

Interestingly NuGet.VisualStudio should not be affected because that assembly version is always 1.0.0.0. When I created NuGet.VisualStudio.Contracts.dll, I debated whether or not we should do the same, but decided not to because N.VS contained mostly COM embeddable interfaces, whereas N.VS.C would not.

The bar to make servicing changes is high, so I'm not sure if we'll be able to insert a higher build of NuGet into dev17.0 to fix this, but I'll ask around. I'll also try to make sure this can't happen again in dev17.1 or later.

NuGet's contracts have not changed between dev16.11 (Nuget 5.11) and dev17.0 (NuGet 6.0), so it's safe from an API perspective to use that version for your extension development, as a workaround. I just hope there aren't package version problems using the 17.0 VS SDK with NuGet.VisualStudio.Contracts 5.11.0, since it will have some dependencies from the 16.x VS SDK.

reduckted commented 3 years ago

NuGet's contracts have not changed between dev16.11 (Nuget 5.11) and dev17.0 (NuGet 6.0), so it's safe from an API perspective to use that version for your extension development, as a workaround.

Awesome, I'll stick with 5.11 for now. Thanks for your help, @zivkan 😄

zivkan commented 2 years ago

Visual Studio 17.0.2 was released today, and it should no longer have a problem with NuGet.VisualStudio package version 6.0.0.