So far, I’ve identified that the version of System.Reflection found in the NuGet package is version 4.1.1.0 and the version in the global assembly cache is version 4.0.0.0 - so where is the version that the build process is choosing?

Returning to the build log, I used the structured log viewer to search for system.reflection.dll under(dfcmd) and worked my way through all 223 occurrences.

As a part of the command line provided to csc.exe (the C# compiler itself), I found this option:

/reference:"C:\Program Files (x86)\Reference Assemblies
    \Microsoft\Framework\.NETFramework\v4.7.2\Facades
        \System.Reflection.dll"

(Note that I’ve formatted the path with some linebreaks.)

Using the same PowerShell command as before, I checked the actual version of this assembly:

PS> [Reflection.AssemblyName]::GetAssemblyName("C:\...\System.Reflection.dll")

Version        Name
-------        ----
4.1.2.0        System.Reflection

That’s exactly the assembly I wanted - and the clue that I needed to solve the problem.

Having the assembly file System.Reflection.dll omitted from the build output directory is totally correct - because it’s a part of the installed .NET runtime, and doesn’t need to be separately deployed.

Since this is the case, I conjectured that I didn’t need to separately reference it from the dfcmd project - and so I removed both the package reference from the .csproj file, and the related binding redirects from the app.config file.

After a full rebuild, I ran the same command as before:

PS> .\dfcmd.exe
Document Factory
No mode specified
Complete (27 milliseconds)

Success!

So what are the lessons here?

I’m reminded that the version of a NuGet package doesn’t necessarily predict the versions of the assemblies found inside.

And, sometimes, the assembly binding redirects introduced into app.config are the problem, not the solution.