_From @fubar-coder on April 5, 2017 15:59_
Currently, when a NuGet package reference is added, there is no way to set the alias from the project system for the new style csproj projects.
This feature is needed, because NuGet package references don't result in direct assembly references any more and only those can have an alias.
My proposal is to add the alias(es) to all assemblies referenced for the NuGet package, but not the indirectly referenced NuGet packages.
_Copied from original issue: dotnet/project-system#1930_
notes
https://github.com/dotnet/sdk/issues/10947 The build tasks on (.NET Core SDK side)
https://github.com/dotnet/NuGet.BuildTasks/issues/70 The build tasks for the non-SDK based PackageReference
https://github.com/dotnet/project-system/issues/6011 Nomination updates on project-system side.
Great suggestion. I'll move this over to the NuGet repo, they own the syntax for PackageReference.
This would be great since we are running into this issue due to https://www.nuget.org/packages/StackExchange.Redis/ and https://www.nuget.org/packages/StackExchange.Redis.StrongName/
We have this issue to if we want to add both of these
https://www.nuget.org/packages/Hl7.Fhir.DSTU2
https://www.nuget.org/packages/Hl7.Fhir.STU3
@brianpos exactly the same problem here.
We would like to run both fhir versions next to each other.
@wburgers i have a project using the extern alias trick https://github.com/brianpos/FhirPathTester but do have to set manually after inclusion. So can't wait for this to be fixed.
+1, can't wait for this. I think this is important because right now different packages implementing same namespaces.
@brianpos could you share your trick please?
IIRC you can configure NuGet to store the NuGet packages in an packages folder instead of using the cache in the users home folder using the NuGet.config. This allows you to manually specify project-relative assembly references (via Reference Update
) which can include the alias. The problem with this approach is, that you have to update the reference whenever the package version changes. However, you can restore the packages into folders without the package version of you don't have conflicting package versions.
Ok, so basically referencing the .dll quasi-manually and including then the alias, right? I was also thinking about that as a work around
As a side note, a workaround is to write a target that does something like this: https://github.com/dotnet/project-system/blob/master/build/Targets/VSL.Imports.targets#L340.
Here we're setting the EmbedInteropTypes metadata, but the same could be applied to alias.
It would be great if someone on the NuGet team could point in the right direction of the repo that requires the change. I am sure people are happy to look if they know where to look.
I think they'll need a design first, I don't see a flushed out proposal on syntax/behavior. Once we do that, we'll then need to work with the project systems (http://github.com/dotnet/project-system) and SDK (http://github.com/dotnet/project-system) to add/respect the property so that it can be passed to NuGet/show in Properties, etc, added to the <REference/>
when we dynamically produce them, and then plumbed through pack (what does an aliased set of references do when we add the package as a reference in the nuspec?). Just to set expectations, this isn't a couple of lines change.
I'll help sponsor, point to the locations that need to change in SDK, ProjectSystem (NuGet team will need to jump in on the nuget side, as I don't have context), and driving this through, if a community member wants to pick up the design/changes.
After some experimentation and got it to work.
Placed the below snippet into my csproj file where I had both references of StackExchange.Redis
and StackExchange.Redis.StrongName
nuget dependencies.
<Target Name="ChangeAliasesOfStrongNameAssemblies" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'StackExchange.Redis.StrongName'">
<Aliases>signed</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
@gertjvr sounds great! I will give it a try over here
if a community member wants to pick up the design/changes.
What would that entail? Sorry, I am not very familiar with the process!
@gertjvr Just to clarify: ChangeAliasesOfStrongNameAssemblies
is just a random target name? Does it need to be called/targeted somewhere?
@fubar-coder the target name is just a random name same goes for the alias signed, the snippet above was all I added to my csproj file.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.6.0" />
<PackageReference Include="AutofacSerilogIntegration" Version="2.0.0" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.1.5" />
<PackageReference Include="AWSSDK.S3" Version="3.1.7.2" />
<PackageReference Include="ConfigInjector" Version="2.2.1175" />
<PackageReference Include="Linq2DynamoDb.DataContext" Version="2.0.0" />
<PackageReference Include="Linq2DynamoDb.DataContext.Caching.Redis" Version="2.0.0" />
<PackageReference Include="MassTransit" Version="3.5.7" />
<PackageReference Include="Microsoft.AspNet.SignalR.Core" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNet.SignalR.Redis" Version="2.2.2" />
<PackageReference Include="Microsoft.Owin" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Owin" Version="1.0.0" />
<PackageReference Include="Serilog" Version="2.5.0" />
<PackageReference Include="ThirdDrawer" Version="1.1.9" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<Target Name="ChangeAliasesOfStrongNameAssemblies" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'StackExchange.Redis.StrongName'">
<Aliases>signed</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
</Project>
This workaround should be added to the official docs.
@nkolev92 @anangaur
I've tried this out with the Fhir assembly @ewoutkramer, works a treat.
Agree this should be in official docs
@gertjvr Sorry, I'm really confused by your workaround. You say you have "both references of StackExchange.Redis and StackExchange.Redis.StrongName nuget dependencies" but then in your example csproj
file, there is no PackageReference
to either of these nuget packages. So..... where are the dependencies? And what does StackExchange.Redis.StrongName
refer to? A namespace? The name of the DLL file that you will be using with extern alias
or something? It's all very unclear.
@jez9999 One of the packages referenced by that csproj references the non strong named version and one references the strong named version. You can't see that reference since PackageReference
only shows the first level reference.
MSBuild enumerates all the direct and otherwise references and adds them.
StackExchange.Redis.StrongName
refers to the file name of the DLL that needs to have the aliases property updated.
I hope that helps.
Thank you @gertjvr for the workaround!!! I've been going nuts for the past couple hours trying to figure out how to work around the StackExchange.Redis.StrongName
dependency in the Microsoft.AspNetCore.All
metapackage...when my code and all the third party packages I use reference the non-strong-named version. Your workaround at least lets me side-step the issue for now.
I'm getting the same issue with FSharpx.Extras
and FSharp.Control.AsyncSeq
both declaring FSharp.Control.AsyncSeq
. Unfortunately in F# there is no such thing as extern alias
. Would it be possible to implement an alias for NuGet packages directly referencing the package without the need of extern alias
? An alias to redirect to types into a specific package, making them available only thorough that alias.
I took @gertjvr 's solution and made it a bit more generic. you can drop this snippet into Directory.Build.targets
:
<Target Name="AddPackageAliases" BeforeTargets="ResolveReferences" Outputs="%(PackageReference.Identity)">
<PropertyGroup>
<AliasPackageReference>@(PackageReference->'%(Identity)')</AliasPackageReference>
<AliasName>@(PackageReference->'%(Alias)')</AliasName>
</PropertyGroup>
<ItemGroup>
<ReferencePath Condition="'%(FileName)'=='$(AliasPackageReference)'">
<Aliases>$(AliasName)</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
and then use it in your csproj like this:
<ItemGroup>
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.6" Alias="signed" />
</ItemGroup>
Does anyone know when this will actually be fixed? Seems like you can have aliases on ProjectReferences but not PackageReferences.
Adding to the mix... I just ran into this problem between Microsoft.Extensions.Primitives
and the NuGet libraries themselves because the NuGet libraries are linking the source files via a submodule rather than referencing the package. I'm not sure what the rationale behind that is, but it led to me needing an alias.
To expand upon @davethieben solution, if you need an alias for a transitive dependency, then this will do the trick:
<Target Name="AddTransitivePackageAliases"
AfterTargets="AddPackageAliases"
Outputs="%(TransitivePackageReference.Identity)">
<PropertyGroup>
<AliasPackageReference>@(TransitivePackageReference->'%(Identity)')</AliasPackageReference>
<AliasName>@(TransitivePackageReference->'%(Alias)')</AliasName>
</PropertyGroup>
<ItemGroup>
<ReferencePath Condition="'%(FileName)'=='$(AliasPackageReference)'" Aliases="$(AliasName)" />
</ItemGroup>
</Target>
Then you can just add an entry in your source project as:
<ItemGroup>
<TransitivePackageReference Include="Microsoft.Extensions.Primitives" Alias="primitives" />
</ItemGroup>
TransitivePackageReference is a made up item name and can technically be anything, but I thought that was appropriately named enough. Being able to use something like:
<ItemGroup>
<PackageReference Update="Microsoft.Extensions.Primitives" Alias="primitives" />
</ItemGroup>
would be better, but I couldn't make it work. I presume that's because the items aren't directly resolved in the current project (since they're transitive). If this ever gets fixed, this is how we'd want aliases for transitive dependencies to be defined IMO.
It's common for packages to carry more than one assembly.
What do you think the behavior is in that case?
Because of the flexibility it allows (specifically only alias one of many assemblies), I'd be more in favor of having a separate item/target than putting this on a PackageReference.
Note that a more complete solution is ensuring that the assembly is coming from the right package, like the following example.
<Target Name="AddCustomAliases" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'ClassLib2' AND '%(ReferencePath.NuGetPackageId)' == 'ClassLib2'">
<Aliases>ClassLib2</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
@nkolev92 fair, though I'd submit that need tends to be quite rare. It's reasonable to consider to make the feature _complete_. In that case, I would suggest something like:
<ItemGroup>
<ReferenceAlias Include="Companion"
PackageReference="Microsoft.Extensions.Primitives"
Assembly="CompanionLibrary" />
<ReferenceAlias Include="Sys" Reference="System" />
</ItemGroup>
This would provide a new type of metadata that could be applied to any reference, including transitive dependencies. In this example, PackageReference and Reference are mutually exclusive and indicate the source of the alias (unambiguously). If Assembly is specified, then the alias only applies to that assembly; for example, in a NuGet package. If Assembly is not specified, then the alias applies to all assemblies in the referenced source (which could be one or many).
I'm pretty sure that item metadata cannot subsequently have its own attributes. I believe you can use nested XML as the body of metadata, but it will be treated as literal text. This would probably require a custom build task to match things up prior to compile, but it _should_ be pretty straight forward.
Defining aliases inline is best IMO, but making that work within the conventions of MSBuild might work the way we'd like.
Just a suggestion...
@commonsensesoftware
That's similar to the thought process I had. :) I just wanted to validate it by the MSBuild/SDK/Build targets folks.
Why the need for a Reference
though?
If you have a reference, you can already use the Aliases metadata.
Ah - yes. You're correct. I guess that wouldn't be needed then. To make things completely specific to NuGet only, then something like this would make more sense:
<ItemGroup>
<PackageReferenceMetadata Include="Microsoft.Extensions.Primitive" Alias="primitives" />
<PackageReferenceMetadata Include="Microsoft.Extensions.Primitive"
Alias="companion"
Assembly="CompanionLibrary" />
</PackageReferenceAlias>
The first form provides an alias for all libraries in the package, while the second only provides an alias for a single package.
Using the more generic name PackageReferenceMetadata (or something like that) could open the door to solve other, existing issues such specifying NoWarn on transitive dependencies. Effectively, there needs to be a way to _bolt on_ certain types of settings and configuration that are currently not supported or cannot be provided by the intrinsic item capabilities of Include or Update.
I'm not sure how NoWarn will be affected by this, but I think that's a fair proposal.
I'll write up some things and start a discussion across the different products.
In the meantime, I'll document the example in samples repo.
What I'm saying is that there is no way to specify NoWarn for a transitive dependency. This _could_ be potentially achieved with:
Use the Update directive for an existing <PackageReference />
that sets NoWarn
<ItemGroup>
<PackageReference Update="Microsoft.Extensions.Primitives" NoWarn="NU###" />
</ItemGroup>
Use <PackageReferenceMetadata />
to set NoWarn and other metadata such as Alias
<ItemGroup>
<PackageReferenceMetadata Include="Microsoft.Extensions.Primitives" Alias="primitives" NoWarn="NU###" />
</ItemGroup>
I don't want to go completely off-topic here. I merely wanted to illustrate that there are other pieces of metadata that currently suffer from the same problem and should be _considered_ in a complete solution.
What I'm saying is that there is no way to specify NoWarn for a transitive dependency.
Hey all,
You can take a look at our design for this here:
Design document.
Hi @nkolev92 ,
Is there a way to alias three packages which have the same namespace. I have tried the design document mentioned solution, but I can't able to achieve that.
I have tried below solution,
<Target Name="AddPackageAliases" BeforeTargets="ResolveReferences" Outputs="%(PackageReference.Identity)">
<PropertyGroup>
<AliasPackageReference>@(PackageReference->'%(Identity)')</AliasPackageReference>
<AliasName>@(PackageReference->'%(Alias)')</AliasName>
</PropertyGroup>
<ItemGroup>
<ReferencePath Condition="'%(FileName)'=='$(AliasPackageReference)'">
<Aliases>$(AliasName)</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
<ItemGroup>
<PackageReference Include="KeyGen_Old.Core" Version="1.0.0" Alias="OldCore" />
<PackageReference Include="KeyGen.Core" Version="2.0.0" Alias="Core" />
<PackageReference Include="KeyGen_Latest.Core" Version="3.0.0" Alias="LatestCore" />
</ItemGroup>
But only one alias is working, Other aliases are not working and showing below error,
The extern alias 'LatestCore' was not specified in a /reference option
@dhinesh-v That workaround is not handling the cases where the assembly name is not equal to the package name.
Specifically:
<ReferencePath Condition="'%(FileName)'=='$(AliasPackageReference)'">
@nkolev92 Thanks for mentioning this case. But all the packages that i'm using has single assembly in it and named same as the package. So the mentioned case didn't cause any problem in my project.
Using this workaround, I can able to alias three assemblies which has different namespaces.
But I'm unable to alias three assemblies which has same namespaces.
Can you please let me know that aliasing three assemblies which has same namespaces is possible or not.
@dhinesh-v
Adding aliases to multiple assemblies are independent actions.
So both scenarios you mentioned are possible.
Hey all,
the NuGet.Client side change has been merged.
However, this is a multi product feature. So I'm working on making sure the experience works end to end before I close this issue.
You can track the effort in the following issues:
https://github.com/dotnet/sdk/issues/10947 The build tasks on (.NET Core SDK side) (https://github.com/dotnet/sdk/pull/11451)
https://github.com/dotnet/NuGet.BuildTasks/issues/70 The build tasks for the non-SDK based PackageReference
https://github.com/dotnet/project-system/issues/6011 Nomination updates on project-system side.
With https://github.com/dotnet/sdk/issues/11612 and https://github.com/dotnet/sdk/pull/11954, finally all the legs of this feature are completed.
The feature will be available in 16.7 and all the matching tooling versions (3.1.400 of the SDK, 5.0 of the SDK) & 5.7 of NuGet.exe.
If you are using NuGet.exe, keep in mind that you need VIsual Studio/ MSBuild to be 16.7
Awesome!! Thanks @nkolev92 for consistently following up on this one 馃憦
@nkolev92 can you please provide a link to the documentation / sample?
@TFTomSun Design at https://github.com/NuGet/Home/blob/dev/designs/PackageReference-Extern-Alias.md.
I'll update the docs by the time this ships in a GA release.
@nkolev92 are the aliases transistive? I mean, are they applied to indirect, transistive dependencies, too?
They only apply to the package reference they are specified on.
The challenge with applying to transitive dependencies is the diamond dependency
scenario where: Project -> A -> C and Project -> B -> C.
For the time being the answer to how do I apply metadata to a transitive package still remains elevate it to top level
.
Hi!
We're waiting for Visual Studio 16.7 to use this feature, do you know if it's available yet in any of the VS16.7 previews? Saw that Preview 3 was just released.
@YanerTavuz Preview 3 should have it
Got it to work with VS16.7 Preview 3.1 and .NET 5 Preview 6 with a .NET project SDK style.
Trying to get it to work with .NET Framework 4.8 with old csproj style,, any tips on how to get it working there? @nkolev92
It works when the csproj is SDK-style, but our solution projects uses the old style.
Edit: After some investigating, it seems that the dll for Microsoft.NuGet.Build.Tasks that is shipped with Visual Studio 16.7 Preview 3.1 does not contain your changes from https://github.com/dotnet/NuGet.BuildTasks/issues/70 so I guess that's the issue.
Will this work to alias different versions of the same package?
Will this work to alias different versions of the same package?
Yes
No, this cannot be used to alias different versions of the same package that has the same identifier. This is entirely used to alias assemblies within a package.
Most helpful comment
After some experimentation and got it to work.
Placed the below snippet into my csproj file where I had both references of
StackExchange.Redis
andStackExchange.Redis.StrongName
nuget dependencies.