How to write a NuGet package that updates the binding redirects when the package reference is upgraded? - nuget

We use VS 2017 and consume NuGet packages the old way. We do not use PackageReference as of yet.
When a NuGet package reference is updated through the NuGet Manager in VS, the respective assembly binding redirect is not updated automatically - we have to do it manually.
So, I suppose it is up to the package to take care of it through the tools\install.ps1 script. Now, I think I know how to implement it, but I do not want to invent the wheel. Surely the code already exists somewhere, but where?
Clarification
Our application is strongly signed and it targets .NET 4.5.2 currently (soon to be upgraded to 4.7.2). We use packages.config.
I need to explain what is going on. There are three players on the field:
A tool - DbUpgrade
The tool plugin Api - DbUpgradeApi
An implementation of the plugin Api - LogDbUpgradeProgress
Let us inspect the DbUpgradeApi package. 3 versions of it are relevant to us:
The version against which LogDbUpgradeProgress is compiled - A
The version against which DbUpgrade is compiled - B
The latest version of DbUpgradeApi - C
The DbUpgrade tool loads the plugin LogDbUpgradeProgress at runtime. The binding redirects are needed, because A is not the same as B (and because the code is signed, nothing to do here currently)
If C is higher than B, then we should update the reference to DbUpgradeApi in DbUpgrade. But doing so must be accompanied with updating the binding redirect. And this is the essence of this question.

Ok, so I just spent the last hour testing, and I didn't need to do anything that I consider special for binding redirects to work.
But first, are you sure you need binding redirects? .NET Core doesn't need it. If you're using .NET Framework, but with a project using PackageReference, then it's resolved at build time, your app.config doesn't need the binding redirect in the version that's checked in with your code, but when you build and check the [your exe name].config file it does have the binding redirects. Also, binding redirects only matter when your assembly has strong naming. If you didn't sign your assembly, then binding redirect isn't needed.
Here are the steps that I took to create a reproduction of getting binding redirects in a console app using packages.config.
Create an empty folder to start with. I used dotnet new sln, dotnet net nugetconfig to generate a new sln and nuget.config file. I edited the nuget.config file to add the folder localFeed as a source, and set the globalPackagesFolder to gpf so I didn't pollute my real global packages folder with test packages. Also created a strong name key with sn -k snk.snk.
Create first test classlib. dotnet new classlib -n someLib. I edited Class1.cs to change the class name to SomeClass and added a property that retusns the value "Version 1". Used Visual Studio to set snk.snk as the signing key. dotnet pack to generate V1 of the package. I then edited SomeClass to change the message to "Version 2" and then ran dotnet pack /p:version=2.0.0. Finally, used nuget.exe add -source localFeed someLib\bin\Debug\someLib.1.0.0.nupkg and again for v2 of the nupkg.
Create the second test classlib, dotnet new classlib -n anotherLib and set the signing key to snk.snk. Changed Class1.cs to AnotherClass and added a property public string Message => new someLib.SomeClass().Message;. Added a reference to someLib version 1 in the csproj, then built, packed and used nuget.exe to add the nupkg to localFeed.
Opened Visual Studio and created a .NET Framework console app. Added a reference to anotherLib, which automatically brought in v1 of someLib. Upgraded the reference of someLib to v2, and confirmed that packages.config now has a binding redirect for someLib.
Created another .NET Framework console app and did the same as step 3, but this time using PackageReference instead of packages.config. The project app.config doesn't have binding redirects, but the .config file in the bin folder after build does.
edit: perhaps an important part to understanding NuGet/MSBuild binding redirect behaviour is the following: In both steps 3 and 4, if I add a reference only to anotherLib, then no binding redirect is created because all assembles that reference someLib reference the same version. Only by making my console app reference a different version of someLib than anotherLib uses, then the binding redirect is created.
In an app with plugins, the building the app assembly won't have a binding redirect, because it's the only assembly in the compile command line that uses the plugin contract dll, so no conflict to need a binding redirect. When the plugin assembly is built, only the plugin depends on the plugin contract dll, so again no conflict so no binding redirect. If you copy all the dlls into a single folder, then there can be a conflict in the required version, but this is a deployment time issue, not a build/compile time issue, so build tools may not help. One way to resolve this would be to add a reference to the plugin project from the app assembly. This way at compile time the build tools can see that two different versions of the plugin contract dll is used, so a binding redirect can be created. This only works if you build the app assembly. If the app is just a binary that you're given, then changing the binding redirects becomes a deployment time responsibility, so development/build tools may not help.

Related

AutoMapper 6.2.2 restore fails on VSTS build server

The Restore step fails in my build definition with this error:
error NU1100: Unable to resolve 'AutoMapper (>= 6.2.2)' for '.NETCoreApp,Version=v2.0'.
This is in a .NETCoreApp 2.0 that is using AutoMapper. Restore succeeds locally in VS 2017. Locally I am using dotnet sdk 2.0.0, and nuget 4.3.1. On the VSTS (cloud) server, I am specifying the latest nuget version, 4.5.0. I have tried many other versions, including 4.3.1 with no success.
I know there is an older, similar question here, but that was never answered satisfactorily, in my opinion.
If I look at the meta data in VS, at the top of one of the Automapper files I can see this:
Assembly AutoMapper, Version=6.2.2.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005
// C:\Users\randyg.nuget\packages\automapper\6.2.2\lib\netstandard1.3\AutoMapper.dll
From this you can see it's taking the netstandard1.3 version, which is correct. I'm wondering if the nuget on the build server is looking for a netcoreApp2.0 folder, rather than the netstandard1.3 folder? Following this theory, I tried adding this to my .csproj file:
<AssetTargetFallback>$(AssetTargetFallback);netstandard1.3</AssetTargetFallback>
My theory was this would tell nuget to look for a netstandard1.3 version of automapper if it couldn't find a netcoreapp2.0 version. However, this didn't have any effect. I later read that behind the scenes this fallback is already added automatically.
If anyone has any ideas I would greatly appreciate it. I'm pulling out (what's left of) my hair on this, as this is the only package giving me trouble, and it's a critical one for my project.
On the build server I'm using .NET Core sdk 2.0.3 as well, if that matters.
This is how I solved this: since it seemed it might have to do with my target being netcoreapp2.0, I tried adding an additional 'dummy' project to my solution, with its target = netstandard1.3. In this configuration, the restore/build succeeded. I thought I would have to maintain this dummy project for this purpose, until I tried deleteing it and now the netcoreapp2.0 solution still restores fine without it. I can only assume something was corrupted in the TFS build server NPM cache (even though I had tried checking the box to disable that cache), and once I got it to succeed once, it has cleared up whatever the issue was.

Visual Studio 2017, Service Fabric project, Sdk Targets: Error when packaging SF project

The error displayed during build:
error MSB4102: The value "" of the "Project" attribute in element is invalid. Parameter "path" cannot have zero length.
Reproduction steps are simple:
VS 2017 RC 2.
Create new Service Fabric Application targeting ASP.Net Core using Web API.
Then attempt to package the service fabric project.
I suspect there is a bug in Microsoft.VisualStudio.Azure.Fabric.ApplicationProject.targets that is part of VS 2017.
The one that sits in this directory: C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\VisualStudio\v15.0\Service Fabric Tools
In the sdk targets file, there is this statement.
<PropertyGroup Condition="'$(LanguageTargets)' == ''">
<LanguageTargets Condition="'$(MSBuildProjectExtension)' == '.csproj'">$(MSBuildToolsPath)\Microsoft.CSharp.targets</LanguageTargets>
<LanguageTargets Condition="'$(MSBuildProjectExtension)' == '.vbproj'">$(MSBuildToolsPath)\Microsoft.VisualBasic.targets</LanguageTargets>
</PropertyGroup>
<!-- TODO: Generate error if LanguageTargets property isn't set here. This would happen, for example if an .fsproj referenced the .NET Sdk
but not the FSharp one. See https://github.com/dotnet/sdk/issues/448 -->
<Import Project="$(LanguageTargets)"/>
A little tough to decipher if you aren't used to it. But. In essence it is saying if it's a .csproj load the CSharp targets, if it is a .vbproj load the VB targets, if neither do nothing.
Then the next line tries to import the targets specified.
In the case of a Service Fabric project, that is a .sfproj, which means LanguageTargets is left blank and we get the error shown at the top of this post. The SF targets file should set LanguageTargets I think, before it reaches this line.
I suspect this sdk.targets file is some default template that hasn't been updated to handle VS 2017 service fabric projects yet.
Possible answer, but I don't like the idea of editing the Sdk.targets file installed by VS. Changing this line in Sdk.targets gets past this error, and doesn't cause any other errors in the build:
From: Matt Thalman at [https://social.msdn.microsoft.com/Forums/en-US/19fd8e9c-a517-4361-b50d-656d679d9c8b/visual-studio-2017-service-fabric-project-sdk-targets-error-when-packaging-sf-project?forum=AzureServiceFabric]
This issue should be fixed in the latest version of VS 2017 RC. Be sure you're running the latest.
However, even with the newest version, it is possible you can see this issue in cases where VS or some automated logic has downloaded the Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.4.0 NuGet package rather than using the package that ships in the box. The version of the NuGet package that ships in the box with VS 2017 RC has the fix for this issue. But the Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.4.0 NuGet package that exists in NuGet.org does not have the fix. There's currently an update to the NuGet package (version 1.4.1) containing a fix that exists but is unlisted. So if you need to make use of that updated package, you'll need to make some manual changes to your Service Fabric Application project:
Update the packages.config file so that it references version 1.4.1 of the Microsoft.VisualStudio.Azure.Fabric.MSBuild package.
Update your .sfproj file by replacing instances of "Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.4.0" with "Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.4.1".
Normally, NuGet would make the project file updates for you automatically when you update the NuGet package version but due to an issue that NuGet has with the Service Fabric Application project type, those updates do not happen automatically which is why you need to make these changes manually (and also why the package is defined as unlisted in NuGet.org).

Visual Studio Team Services: Build universal app for the store

I want to build universal app for release on the store i.e. generating the *.appxupload (building the *.appx to deploy on my machine is fine; have to remove the default /p:AppxBundle=Always though and cannot build Win32: no matter what I do, it always builds ARM). But I cannot get it to work at all following Microsoft's instructions i.e. adding /p:UapAppxPackageBuildMode=CI.
The system simply stops with
Error MSB4126: The specified solution configuration "Release|x86" is invalid. Please specify a valid solution configuration using the Configuration and Platform properties (e.g. MSBuild.exe Solution.sln /p:Configuration=Debug /p:Platform="Any CPU") or leave those properties blank to use the default solution configuration.
My project has obviously an Release|x86 configuration. And it reports this while building ARM release. I tried changing x86 to Win32 but it then report
APPX0502: File 'C:\a\1\s\AppName\BundleArtifacts\Win32.txt' not found. [C:\a\1\s\AppName\AppName.vcxproj]
EDIT: Apparently, I have to manually set the Project to Win32 for it to build x86 release. Previously, it was left blank (and the automatic build configuration generator also leaves it blank).
Update the argument to:
/p:UapAppxPackageBuildMode=StoreUpload
And then queue the build, you should get the appxupload file.
Refer to this link for details: Windows Store app projects stopped generating the .appxupload file.
Similar question here: VSTS build for UWP app not producing a .appxupload file.

What controls the Specific Version property of a reference in a NuGet package?

We have many libraries which are build in a CI, which deploy prereleases every time they build. Other projects depend on these and automatically updates them during build.
But the references are set with the Specific Version = true, which means that increasing the version number on these dlls causes the build to fail.
How can I control the setting of the property?
You cannot change NuGet's behaviour without changing its source code. NuGet will always sets SpecificVersion to true when adding a non-GAC assembly from within Visual Studio.
You would need to run some sort of post build script to fix the references or manually change them.
Not sure exactly how you are updating the projects in your CI server. If you use NuGet.exe update project.csproj then that will update to the latest NuGet package and will not set SpecificVersion to true. However the command line application does not support PowerShell scripts or content files, only references will be updated. This also assumes that there is an update available otherwise the reference will not be modified.

Visual Studio 2010 Publish Web feature not including all DLLs

I have an ASP.NET MVC 2 application.
Web project contains a reference to SomeProject
SomeProject contains references to ExternalAssembly1 and ExternalAssembly2.
SomeProject explicitly calls into ExternalAssembly1, but NOT ExternalAssembly2.
ExternalAssembly1 calls into ExternalAssembly2
When I perform a local build everything is cool. All DLLs are included in the bin\debug folder. The problem is that when I use the Publish Web command in Visual Studio 2010, it deploys everything except ExternalAssembly2.
It appears to ignore assemblies that aren't directly used (remember, ExternalAssembly2 is only used by ExternalAssembly1).
Is there any way I can tell Visual Studio 2010 to include ExternalAssembly2?
I can write a dummy method that calls into ExternalAssembly2. This does work, but I really don't want to have dummy code for the sole purpose of causing VS2010 to publish the DLL.
None of these answers are sufficient in my mind. This does seem to be a genuine bug. I will update this response if I ever find a non-hack solution, or Microsoft fixes the bug.
Update:
Doesn't seem promising.
https://connect.microsoft.com/VisualStudio/feedback/details/731303/publish-web-feature-not-including-all-dlls
I am having this same problem (different assemblies though). If I reference the assemblies in my web project, then they will get included in the publish output, but they should be included anyway because they are indirect dependencies:
Web Project ---> Assembly A ---> Assembly B
On build, assemblies A and B are outputed to the \bin folder. On publish, only assembly A is outputed to the publish folder.
I have tried changing the publish settings to include all files in the web project, but then I have files in my publish output that shouldn't be deployed.
This seems like a bug to me.
I had the same problem with VS2010 and a WCF Service Application.
It turns out that if your (directly or indirectly) referenced DLL's are deployed to GAC, the VS publishing feature excludes them. Once I removed the assemblies from GAC, publishing feature started working as expected.
I guess VS is assuming that if your assemblies can be located in GAC on the machine you build, they will be located in GAC on the target machine as well. At least in my case this assumption is false.
My tests show that the external assemblies get published when I have a reference on them in the web project. I do not have to write any dummy code to make it work. This seems acceptable to me.
I agree with Nicholas that this seems to be a bug in visual studio. At least it escapes me what the reason for the behavior could be.
I have created this issue as a bug on Microsoft Connect. If anyone experiencing it could vote it up https://connect.microsoft.com/VisualStudio/feedback/details/637071/publish-web-feature-not-including-all-dlls then hopefully we'll get something done about it.
If you go into the ExternalAssembly2 reference property list and change the "Copy Local" to "True" i think that might solve your issue.
I don't know if you are watching this still but I found the solution (I had the exact same issue) via this MSDN article. Under "build action" for the file choose "Content" that should include it in the list of files publish brings over.
I have created a new Connect bug here https://connect.microsoft.com/VisualStudio/feedback/details/731303/publish-web-feature-not-including-all-dlls
I've also attached a solution and detailed steps to reproduce this issue. Lets hope this time they won't close it as Can't Reproduce.
Vote for this connect issue if you experience the missing dll problem.
Copy local did the trick. I had an issue that the Newtonsoft.Json assembly get included in the deploymeny package. Copy local was set to false.
I am experiencing the same type of issue with a web project. I have a web project that references assembly A which references assembly B. It worked fine for some time but today it was broken. I did a rebuild of the solution and this time it deployed everything correctly.
I had this same problem today. I published my web project and realized that not all of the reference DLL's were there. In particular, the indirect DLL references.
It turns out that the directory in which I was publishing to was out of disk space (network share). I had just enough space to publish all the files except for few indirect reference DLL's. The sad part is that VS08 didn't throw any errors. It just published the files are usual. I cleared out some HDD space and everything worked fine.
I didn't find the HDD space issue until I tried to manually move the DLL's over.
in my case it is quite tricky.
Reference to ExternalAssembly2 is not required to Build the project but vital for run-time since we use reflection to configure Unity container.
So, I delete the reference - build the project successfully, but get run-time error.
If I preserve the reference I can Build and Run the application but I cannot Publish it with ExternalAssembly2 - get run-time exception as well.
This is happen because of internal VS2010 assemblies optimization.
So, what we can do here?
1. Put some unrequired peice of code to use any ExternalAssembly2's class.
2. escape from reflection and use static assemblies linking.
Hope this helps to smbd.
I got the same problem and this is a VS2010 bug if there's a reference link like:
Web Project --> custom project --> assembly1 -->(indirectly) assembly2.
For now I find if I reference the Assembly1 in the web project, then assembly2 is included in the bin folder.
So I had to add an additional reference link like:
Web project --> assembly1 -->(indirectly) assembly2.
Then VS can recognize assembly2 and include its dll file in publish action.