Skip to content

ProjectReference alias support for multiple versions of the same package within 1 project #12400

@tdhatcher

Description

@tdhatcher

NuGet Product(s) Affected

NuGet.exe, MSBuild.exe

Current Behavior

Scenario A. Direct Nuget Dependency

<!-- Api.csproj -->
<ItemGroup>
    <PackageReference Include="Api.Sdk" Version="2.0.0" Aliases="Sdk_v2_nuget" />
    <PackageReference Include="Api.Sdk" Version="1.0.0" Aliases="Sdk_v1_nuget" />
    <PackageReference Include="Api.Sdk" Version="3.0.0" Aliases="Sdk_v3_nuget" />
</ItemGroup>
// Works (first alias declared in .csproj is available)
// Program.cs
extern alias Sdk_v2_nuget;
using v2Models = Sdk_v2_nuget::Api.Sdk.Models;
var request2 = new v2Models.Request();
// Doesn't work (subsequent aliases declared in .csproj ignored and not available)
// Program.cs
extern alias Sdk_v1_nuget; // compiler error: CS0430 The extern alias 'Sdk_v1_nuget' was not specified in a /reference option
extern alias Sdk_v3_nuget; // compiler error: CS0430 The extern alias 'Sdk_v3_nuget' was not specified in a /reference option

using v1Models = Sdk_v1_nuget::Api.Sdk.Models;
using v3Models = Sdk_v3_nuget::Api.Sdk.Models;

var request1 = new v1Models.Request();
var request3 = new v3Models.Request();

Scenario B. Transitive Dependency through direct Project Reference

<!-- Api.csproj -->
<ItemGroup>
    <ProjectReference Include="..\TransitiveProjectB\TransitiveProjectB.csproj" />
    <ProjectReference Include="..\TransitiveProjectC\TransitiveProjectC.csproj" />
    <ProjectReference Include="..\TransitiveProjectA\TransitiveProjectA.csproj" />
</ItemGroup>
// Works and automatically uses Api.Sdk 3.0.0 (the highest version) that finds in any of the other projects directly referenced (ie. TransitiveProjectC)
// 1.0.0 and 2.0.0 is not accessible
// Program.cs
using Api.Sdk.Models;
var request = new Request();
<!-- TransitiveProjectA.csproj -->
<ItemGroup>
    <PackageReference Include="Api.Sdk" Version="1.0.0" />
</ItemGroup>
<!-- TransitiveProjectB.csproj -->
<ItemGroup>
    <PackageReference Include="Api.Sdk" Version="2.0.0" />
</ItemGroup>
<!-- TransitiveProjectC.csproj -->
<ItemGroup>
    <PackageReference Include="Api.Sdk" Version="3.0.0" />
</ItemGroup>

Desired Behavior

In Scenario B. There currently appears to be some assumption precedent as 3.0.0 is the version automatically chosen over 1.0.0 and 2.0.0 without instruction.

Scenario A. This would compile and I would continue to expect NU1504 warning from compiler indicating that duplicate 'PackageReference' items found (good attention). Or a compilation error for certain scenarios that make it impossible for reasons beyond my current depth of understanding. At that point the maintainers may have to make a minor patch to update a transitive dependency or whatever is necessary to bring the two major versions of the Api.Sdk package into parity for the two assemblies to work along side of each other. This seems like an extreme edge case though and if to be ignored then in that case default to the behavior it is does today -- not possible.

In the case the transitive dependencies for each version of the same Api.Sdk package, shouldn't they continue to remain isolated like the existing behavior is for the situation when you have two Nuget references to separate packages but each has a transitive dependency on the same package but using a different version? Isn't this one area where Nuget really shines today?

// Works
// Program.cs
extern alias Sdk_v1_nuget;
extern alias Sdk_v2_nuget;
extern alias Sdk_v3_nuget;

using v1Models = Sdk_v1_nuget::Api.Sdk.Models;
using v2Models = Sdk_v2_nuget::Api.Sdk.Models;
using v3Models = Sdk_v3_nuget::Api.Sdk.Models;

var request1 = new v1Models.Request();
var request2 = new v2Models.Request();
var request3 = new v3Models.Request();

Nuget Multiple Version Support

Additional Context

@nkolev92 @commonsensesoftware Continuation of the renewed discussion on a closed issue #4989

@commonsensesoftware mentioned some other strategies for API version affinity here
#4989 (comment)

  1. Include all supported API versions in a single package, separated by namespace
  2. Include a single package that maps to the most recent supported API versions
  3. Include a meta package that maps to individual packages corresponding to independently version clients

There are several ways to mitigate breaking changes in API to facilitate a smooth transition. I was just thinking of what I felt would make life easier and reduce the need for any creativity or extra overhead on the API management and maintenance side of the coin when you need to support multiple versions of an API side by side on the same instance and code base.

API deprecation is a common use case and when it involves breaking changes maintaining N-2 or more is doable, just doesn't feel the cleanest. I just thought perhaps this would just give developers another option and tactic for implementing and managing breaking changes in their API code bases. I'd say this approach is closest to option 2) for the consumer of the Sdk but for the maintainer for the API is closer to option 1) without having to clone all the breaking change classes or reference them in a unique way to maintain a version alongside since a Nuget packages previous version could provide that already. Or duplicate and maintain additional infrastructure. Just another option.

I would like to know where I could learn more about option 3) and a "meta package".

Tear it apart :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Functionality:RestorePriority:3Issues under consideration. With enough upvotes, will be reconsidered to be added to the backlog.Type:DCRDesign Change Request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions