Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extern alias support for NuGet package references #4989

Open
davkean opened this issue Apr 6, 2017 · 33 comments

Comments

@davkean
Copy link

commented Apr 6, 2017

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

@davkean

This comment has been minimized.

Copy link
Author

commented Apr 6, 2017

Great suggestion. I'll move this over to the NuGet repo, they own the syntax for PackageReference.

@andymac4182

This comment has been minimized.

Copy link

commented May 3, 2017

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/

@brianpos

This comment has been minimized.

Copy link

commented May 3, 2017

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

@wburgers

This comment has been minimized.

Copy link

commented May 23, 2017

@brianpos exactly the same problem here.
We would like to run both fhir versions next to each other.

@brianpos

This comment has been minimized.

Copy link

commented May 23, 2017

@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.

@JoseFMP

This comment has been minimized.

Copy link

commented Jun 21, 2017

+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?

@fubar-coder

This comment has been minimized.

Copy link

commented Jun 21, 2017

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.

@JoseFMP

This comment has been minimized.

Copy link

commented Jun 21, 2017

Ok, so basically referencing the .dll quasi-manually and including then the alias, right? I was also thinking about that as a work around

@davkean

This comment has been minimized.

Copy link
Author

commented Jun 21, 2017

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.

@andymac4182

This comment has been minimized.

Copy link

commented Jun 21, 2017

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.

@davkean

This comment has been minimized.

Copy link
Author

commented Jun 21, 2017

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.

@davkean

This comment has been minimized.

Copy link
Author

commented Jun 21, 2017

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.

@gertjvr

This comment has been minimized.

Copy link

commented Jun 23, 2017

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>
@JoseFMP

This comment has been minimized.

Copy link

commented Jun 23, 2017

@gertjvr sounds great! I will give it a try over here

@ewoutkramer

This comment has been minimized.

Copy link

commented Jun 26, 2017

if a community member wants to pick up the design/changes.

What would that entail? Sorry, I am not very familiar with the process!

@fubar-coder

This comment has been minimized.

Copy link

commented Jun 26, 2017

@gertjvr Just to clarify: ChangeAliasesOfStrongNameAssemblies is just a random target name? Does it need to be called/targeted somewhere?

@gertjvr

This comment has been minimized.

Copy link

commented Jun 26, 2017

@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>

jasonmalinowski added a commit to jasonmalinowski/roslyn that referenced this issue Aug 18, 2017

Merge the contents of ServicesVisualStudio.Next into ServicesVisualSt…
…udio

We don't need the split anymore now that we're only supporting Dev15.

Unfortunately this required the introduction of a NuGet package that
has a namespace "Workspace" to our VS project, which meant I had to
disambiguate Workspace wherever it's used in that layer. I considered
doing an extern alias instead to disambiguate, but applying that to a
ProjectReference isn't supported. That's tracked by NuGet/Home#4989.

jasonmalinowski added a commit to jasonmalinowski/roslyn that referenced this issue Aug 25, 2017

Merge the contents of ServicesVisualStudio.Next into ServicesVisualSt…
…udio

We don't need the split anymore now that we're only supporting Dev15.

Unfortunately this required the introduction of a NuGet package that
has a namespace "Workspace" to our VS project, which meant I had to
disambiguate Workspace wherever it's used in that layer. I considered
doing an extern alias instead to disambiguate, but applying that to a
ProjectReference isn't supported. That's tracked by NuGet/Home#4989.

jasonmalinowski added a commit to jasonmalinowski/roslyn that referenced this issue Sep 6, 2017

Merge the contents of ServicesVisualStudio.Next into ServicesVisualSt…
…udio

We don't need the split anymore now that we're only supporting Dev15.

Unfortunately this required the introduction of a NuGet package that
has a namespace "Workspace" to our VS project, which meant I had to
disambiguate Workspace wherever it's used in that layer. I considered
doing an extern alias instead to disambiguate, but applying that to a
ProjectReference isn't supported. That's tracked by NuGet/Home#4989.

jasonmalinowski added a commit to jasonmalinowski/roslyn that referenced this issue Sep 6, 2017

Merge the contents of ServicesVisualStudio.Next into ServicesVisualSt…
…udio

We don't need the split anymore now that we're only supporting Dev15.

Unfortunately this required the introduction of a NuGet package that
has a namespace "Workspace" to our VS project, which meant I had to
disambiguate Workspace wherever it's used in that layer. I considered
doing an extern alias instead to disambiguate, but applying that to a
ProjectReference isn't supported. That's tracked by NuGet/Home#4989.

jasonmalinowski added a commit to jasonmalinowski/roslyn that referenced this issue Sep 6, 2017

Merge the contents of ServicesVisualStudio.Next into ServicesVisualSt…
…udio

We don't need the split anymore now that we're only supporting Dev15.

Unfortunately this required the introduction of a NuGet package that
has a namespace "Workspace" to our VS project, which meant I had to
disambiguate Workspace wherever it's used in that layer. I considered
doing an extern alias instead to disambiguate, but applying that to a
ProjectReference isn't supported. That's tracked by NuGet/Home#4989.

jasonmalinowski added a commit to jasonmalinowski/roslyn that referenced this issue Sep 11, 2017

Merge the contents of ServicesVisualStudio.Next into ServicesVisualSt…
…udio

We don't need the split anymore now that we're only supporting Dev15.

Unfortunately this required the introduction of a NuGet package that
has a namespace "Workspace" to our VS project, which meant I had to
disambiguate Workspace wherever it's used in that layer. I considered
doing an extern alias instead to disambiguate, but applying that to a
ProjectReference isn't supported. That's tracked by NuGet/Home#4989.

@emgarten emgarten added this to the Backlog milestone Oct 17, 2017

@emgarten emgarten added the Type:Docs label Oct 17, 2017

@emgarten

This comment has been minimized.

Copy link
Collaborator

commented Oct 17, 2017

This workaround should be added to the official docs.

@nkolev92 @anangaur

@brianpos

This comment has been minimized.

Copy link

commented Oct 17, 2017

I've tried this out with the Fhir assembly @ewoutkramer, works a treat.
Agree this should be in official docs

@commonsensesoftware

This comment has been minimized.

Copy link

commented Mar 18, 2019

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.

@nkolev92

This comment has been minimized.

Copy link
Member

commented May 10, 2019

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>
@commonsensesoftware

This comment has been minimized.

Copy link

commented May 10, 2019

@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...

@nkolev92

This comment has been minimized.

Copy link
Member

commented May 10, 2019

@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.

@commonsensesoftware

This comment has been minimized.

Copy link

commented May 10, 2019

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.

@nkolev92

This comment has been minimized.

Copy link
Member

commented May 10, 2019

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.

@nkolev92 nkolev92 self-assigned this May 10, 2019

@commonsensesoftware

This comment has been minimized.

Copy link

commented May 10, 2019

What I'm saying is that there is no way to specify NoWarn for a transitive dependency. This could be potentially achieved with:

Option 1

Use the Update directive for an existing <PackageReference /> that sets NoWarn

<ItemGroup>
  <PackageReference Update="Microsoft.Extensions.Primitives" NoWarn="NU###" />
</ItemGroup>

Option 2

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.

@bording

This comment has been minimized.

Copy link

commented May 10, 2019

What I'm saying is that there is no way to specify NoWarn for a transitive dependency.

Yep: #5740

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.