msbuild not copying referenced assembly - msbuild

I have a solution that contains two projects:
- projectA has a nuget reference to ServiceStack ormlite
- projectB has a reference to projectA
When I build the solution the outdir for projectA contains all the assembly coming from nuget packages (4 assemblies), whereas the projectB only copies 2 of them. Obviously when I start it I get an FileNotFoundException.
I have already tried with no success to add private=true flag
I have seen many references to this problem and it gets very confused now about what is going on here (it seems that msbuild does not handle reference the way I think is the only thing I know:().
Any idea what could be done to have a reliable process to build my solution ?

The build will only copy to projectB's output folder the assemblies that are actually used by projectA and result in references in projectA's output assembly, regardless of which assemblies projectA references.
You can open projectA's assembly with Reflector or ildasm and see that of those 4 assemblies only 2 are actually used and referenced.
If the assemblies need to be there at runtime for projectB, add a reference to the NuGet package to projectB as well, or make sure they are copied. This post shows a general-purpose solution, but I haven't tried it.

Related

msbuild refuses to copy unsigned dll if it is of lower version than demanded by one of the project dependencies - is it by design?

I have the following situation:
All the involved dlls are unsigned
All the projects in the solution depend on version 1.0.21221.1 of Shared.dll
Some NuGet dependencies of some projects in the solution depend on version 1.0.21237.1 of the same dll.
When the web application is built (let us name it Api) it is expected to copy Shared.dll from $(OutDir) to the $(OutDir)_PublishedWebsites\Api\bin folder. The Shared.dll found in $(OutDir) has the version 1.0.21221.1.
The Shared.dll is NOT copied and the web application fails to run.
Here is the evidence from the binary log:
Exhibit A - The conflict of versions:
Exhibit B - ResolveAssemblyReference instructs NOT to copy Shared.dll:
I understand that msbuild does not like the idea of conflicting versions, but NOT copying the dll produces a downright bug, because the application fails to start.
I understand one can resolve it by adding an assembly binding redirect. But I thought it was unnecessary for unsigned assemblies. Am I understanding wrong or am I missing something?
EDIT 1
Here are my answers to the questions posted in the comments:
(Unfortunately I was asked to obfuscate some keywords, I do not know why)
How exactly does the Api project reference Shared.dll?
As we can see in the exhibit B Shared.dll is a transitive dependency of Api. Indeed, Api depends on Xyz.BusinessApi like this:
<Reference Include="Xyz.BusinessAPI" />
Now that DLL depends on Shared.dll through the respective NuGet dependency, here is a snippet from the project.assets.json file of Xyz.BusinessAPI:
What other projects reference Shared.dll and how?
There are a lot of projects referencing it as a NuGet package at version 1.0.21221.1. The problem is that some projects also reference two other NuGet packages which in turn depend on the version 1.0.21237.1 of the same NuGet package. This is indicated in the RAR output - see the exhibit A.
I would like to emphasize - no project references Shared.dll as a raw dll, only either as NuGet package or indirectly through other NuGets or projects or project dlls. Project dll is a dll of a project from a previously built solution - we do not allow project references to other solutions, so if a project is built in a previous solution, then it would be referenced as DLL in subsequent solutions.
What is the mechanism used to copy from OutDir to _PublishedWebsites\api\bin?
This is the standard web application publishing target _CopyFilesMarkedCopyLocal from C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VisualStudio\v16.0\WebApplications\Microsoft.WebApplication.targets:
<!-- copy any referenced assemblies to _PublishedWebsites\app\bin folder -->
<Copy SourceFiles="#(ReferenceCopyLocalPaths)"
DestinationFiles="#(ReferenceCopyLocalPaths->'$(WebProjectOutputDir)\bin\%(DestinationSubDirectory)%(Filename)%(Extension)')"
SkipUnchangedFiles="true"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
How does Shared.dll end up in OutDir?
All of our code is built into the shared bin directory - we set OutDir to the same value for all the projects. Thus all the project binaries and their dependencies, including Shared.dll first end up there.
Are there any double-writes in the binlog?
Yes, but I do not think they are relevant:
The short answer is that your projects indirectly depend on two versions of Shared.dll: 1.0.21221.1 and 1.0.21237.1.
RAR (the MSBuild ResolveAssemblyReference task) inspected all the references of all .dlls and found these two versions. It reported a conflict:
Pay attention how it reported the found file path in square brackets for Shared.dll 21221 and reported [] (meaning no file of such version was found) for 21237.
It's useful to search for these using There was a conflict under($rar) or $warning under($rar).
Now, the OutDir only contains the Shared.dll 21221, so the Shared.dll with version 21237 couldn't be found anywhere.
The trick is to search for Shared.dll under($rar project(api.csproj)) You will find the relevant messages from RAR:
Considered "C:\Xyz\61\bin.link\Shared.dll",
but its name "Shared, Version=1.0.21221.1, Culture=neutral, PublicKeyToken=null"
didn't match the expected name "Shared, Version=1.0.21237.1, Culture=neutral, PublicKeyToken=null".
So, it saw a conflict, decided to unify on a later version (21237), but didn't find the file of that version. So the reference of 21221 was not CopyLocal because it didn't unify on that version, and the reference to 21237 was not resolved because a file of that version was not found.
To resolve this, I recommend adding an explicit reference to Shared.dll of the version 21237 (either via NuGet or via GeneratePathProperty metadata on the package reference: https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty). If you use GeneratePathProperty you can then reference the .dll directly using $(PkgFoo_Bar)\lib\net472\Shared.dll or similar. Also add a binding redirect from 21221 to 21237 to resolve the conflict. Once the correct version (21237) will be in your OutDir, it will get copied to output correctly.
Hope this clarifies.

Msbuild not copying a ProjectReference to bin folder

To prevent circular dependency, i had to make a reference from (lets say) project A, to B's bin folder. When i run a rebuild or build in Visual Studio it creates bin folder and required dll references by A, under B project.
But msbuild command does not work that way. It does not create bin and dlls under B. I investigate the problem, found some solutions like using dummy class user method to make msbuild copy references under bin. But it did not work too.
Project A -> Project B/bin/C Dlls ->Project C
Project C Dlls required by Project A.
What do i have to do to make msbuild command create bin folder under B project?
It looks like the circular dependency is still present. It has only been circumvented by going directly to the bin folder. This bypasses the safeguards that call out a circular dependency at build time.
As a general rule, if you need to go directly to the bin folder then there's a problem.
I suggest refactoring the projects to remove the circular dependency.

VSTS build project with reference to other solution

I have a repository that contains two solutions. One solution (in this case solution A) for a web project that has a reference to a project in the second solution (in this case solution B) (in the same repository).
When I build the web project in VSTS I pull the repository, build solution B, and then build solution B.
Build solution B work, but, the build of solution A is failed cause the reference dll of the project in solution B didn't found
You have a few options:
1) Use project references. You don't need to depend on an assembly.
2) Use NuGet packages -- the shared piece is built via a CI process, turned into a NuGet package, and then published to a Packages feed. The dependent projects can reference the NuGet package and restore an appropriate version on build.
Which approach you should take depends on a lot of factors. If you're not worried about versioning, just use project references.
As Daniel said that it’s better to use NuGet packages.
Regarding reference the assembly file directly, refer to these steps:
Open your web project file through Notepad
Find the related reference and check Hintpath value, should be relative path.
Add Copy files task to your build definition (Before build solution A task) to copy corresponding assembly files to corresponding folder (per to that relative path)

Why Msbuild does not copy Newtonsoft.Json.dll referenced by Microsoft.AspNet.WebApi.Client?

My web projectA references my projectB that references Microsoft.AspNet.WebApi.Client that references Newtonsoft.Json assembly. It is not referenced anywhere else. The problem was that on my deployment server Newtonsoft.Json was not copied to bin directory once run as
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe .\src\path.to.Web.projectA.csproj /t:Rebuild /p:Configuration=Release
And it eventually broke with this exception:
FileNotFoundException: Could not load file or assembly
'Newtonsoft.Json, Version=7.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The
system cannot find the file specified.
I've resolved it by adding a stub code to projectB:
string json = JsonConvert.SerializeObject(new Something());
Can someone describe why it was not copied before?
Note. These questions do not seem to answer the question or out of date or I miss something?
MSBuild does not copy directly referenced dlls to bin folder
msbuild not copying referenced assembly
MSBuild doesn't copy references (DLL files) if using project dependencies in solution
http://blog.alexyakunin.com/2009/09/making-msbuild-visual-studio-to.html
Copying a DLL's dependencies in Visual Studio
I made a comment under Alex's blog post,
It does not work for me when the indirect dependency comes from NuGet packages (Microsoft.AspNet.WebApi.Client and Newtonsoft.Json). But the tip described in http://www.paraesthesia.com/archive/2014/05/09/recursively-copying-indirect-project-dependencies-in-msbuild.aspx/ solves the issue. I think it uses a more suitable indirect dependency detection approach.
If you follow that solution, then there is no need to use the stub at all.
About why the dll is not copied over, it has been narrowed down to an MSBuild issue (or more as there are too many reports related), which Microsoft claims won't fix on Connect,
https://connect.microsoft.com/VisualStudio/feedback/details/797034/msbuild-handles-direct-and-transitive-references-differently-for-unused-assembly-references

Best practice for storing and referencing DLL libraries?

Often times a developer on my team will create a new Visual Studio project and reference a DLL somewhere on their local machine (e.g., C:\mydlls\homersimpson\test.dll). Then, when I get the project from the source control repository, I cannot build the project because I do not have the referenced dll in the exact same location on my machine.
What is the best practice for storing and referencing shared libraries?
I typically create a lib folder in my project, where I put the referenced dll's. Then I point the reference to the dll in the lib folder. This way, every developer can build the project after retrieving from source control.
If it's a project that was built in house, you could also add that project to your solution.
If the assembly is not in the GAC, create a directory called dependencies and add all assemblies there. The folder and the assemblies are added to source control. The rule is that given any project in source control, all that is required to build is to do a checkout and build the project (or run some tool that is also checked into the project).
If you add a folder to the solution and add the assemblies to the solution folder, this also provides a visual cue to the devs that indicates what external dependencies are present... all dependencies are in that directory. Relative paths ensure that Visual Studio can locate the references without a problem.
For large solutions, with 20+ projects, this makes life much easier!
Best practice I would expect would have Your SC repository include and enforce the relative locations of referenced objects for you (usually via a shared path), so you aren't dealing with this issue directly. The original developer should check in this information.
If you check in the actual DLLs into source control, then you can reference them by relative path and all developers will automatically get any dependencies when they next update the project.
Adding a DLL reference by full path would be a developer error just as adding a source file by full path would be an error.
Rule of thumb: If the project isn't a part of the solution, reference released dlls from a source controlled /binshare or /lib directory that is under your solution's source control tree. All external dependencies should have versioned DLLs that go in this /binshare directory.
I understand what your co-worker is doing in regards to convenience. However, that developer's approach is diametrically opposed to proper configuration/build management.
Example: If you use the MS Data Application Block as a dependency in your application, you should reference a properly released binary, instead of getting latest from MS's dev source trunk.
I think this approach is quite the opposite of what I would consider best practice. I think it would be a much better approach to keep the third party binaries out of the source repository and reference them through something like a Maven repository in your build process. Putting the dlls in the source repository unnecessarily bloats the contents of the repository and results in gets of projects taking considerably longer then necessary. It also makes the independent management of the third party binaries' versions obfuscated by not referencing the version by name but rather implied by referencing the dll of a particular version stored in the projects lib folder.
Why not set up a private NuGet-feed? This way, there is only a single copy of a dependency (the NuGet repository) and multiple projects can reference it. Multiple versions of the dependency can coexist, and each project can reference a different version, if necessary. Also, TFS Build can restore the packages at build time.
Configuring VS: https://www.visualstudio.com/en-us/docs/package/nuget/consume