Recently, we encountered a challenge in our company’s project development: determining whether the code versions of a C# project are identical using two DLL files. In other words, when the same code is compiled on different machines, will it result in identical binary files?
TL;DR: Binary files won’t be identical because their MVID (Module Version ID) is different.
Initially, the idea seemed straightforward: if the code versions are the same and other environments (compiler, SDK, even the operating system) are consistent, identical binary files should be generated. A misleading fact was that I had compiled the same project multiple times on my laptop, resulting in identical hashed binary files. So, should similar results be expected on different machines? However, reality did not align with my expectations.
I created a new Library project and compiled it on two different machines, obtaining different files. Specifically, although their sizes were the same, the hash values were different.
Eric Lippert‘s blog explained this issue: the C# compiler is designed never to produce identical binaries. The compiler embeds a newly generated GUID (MVID) at every run to ensure no two assemblies are identical.
Inspecting the binary files with dotPeek clearly revealed their different MVIDs.
As for why compiling on the same machine yields the same MVID, I think it’s because the compiler and compilation environment run on the same machine, generating identical assembly content. Thus, compiling the same source code should produce the same MVID.
Returning to the initial question, if comparing the source code using two DLLs, a straightforward method is to check if the files are the same except for the MVID. However, if the files are different, it doesn’t necessarily mean the source code differs; factors such as PDB information or compile-time optimizations may also result in different file hashes.
The ultimately adopted method involves directly including the Git commit hash in the Product Version of the DLL file, confirming the source code version through the Git commit version. For .NET SDK 8 and above, enabling the IncludeSourceRevisionInInformationalVersion tag suffices. For older versions, the NuGet package Unclassified.NetRevisionTask can be used.