Now that we have the new project system, and we can define common assembly info in our .csproj, we can say good-bye to AssemblyInfo.cs, well, I still see these left around for general assembly attributes.

For example it is quite common to see an InternalsVisibileTo.cs file these days, instead of AssemblyInfo.cs, see here for an example.

However what if you want to add your own assembly attribute with a value you want to pass in as an MSBuild property? For example, an AssemblyMetadataAttribute that contains a git commit hash?

Well we are in luck, there is an AssemblyAttribute item we can add to our ItemGroup! If we had a property named CommitHash we could write the following:

<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(CommitHash) != ''" >
<_Parameter1>CommitHash</_Parameter1>
<_Parameter2>$(CommitHash)</_Parameter2>
</AssemblyAttribute>

Great! One last thing, we need to say when this is going to happen, and that is before CoreGenerateAssemblyInfo, so:

<Target Name="AddGitMetadaAssemblyAttributes"
BeforeTargets="CoreGenerateAssemblyInfo">
<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(CommitHash) != ''" >
<_Parameter1>CommitHash</_Parameter1>
<_Parameter2>$(CommitHash)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Target>

Cool. But wait! It doesn't always work. Sometimes when I do a build, the value doesn't change. What gives?

So it turns out in .NET SDK 2 and up, there are lots of measures to improve performance, one of which is the <AssemblyName>.AssemblyInfoInputs.cache file, which appears in the obj folder, and just contains a hash of the AssemblyInfo inputs as you'd expect.

The hash is calculated internally only by the _Parameter1 values, so in our example where we put the varying data in _Parameter2, this doesn't bust the cache. We can see the logic for this here in CreateGeneratedAssemblyInfoInputsCacheFile.

As a hacky workaround, we can temporarily add our values to _Parameter1 then remove them after the hash is generated. Like this:

<Target Name="GitMetadataAssemblyInfoCacheFileFix"
BeforeTargets="CreateGeneratedAssemblyInfoInputsCacheFile" >
<ItemGroup>
<AssemblyAttribute Include="TemporaryAdditionalHashItem" Condition=" $(CommitHash) != '' ">
<_Parameter1>$(CommitHash)</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Target>
<Target Name="CleanupTemporaryAdditionalHashItem"
AfterTargets="CreateGeneratedAssemblyInfoInputsCacheFile;AddGitMetadaAssemblyAttributes" >
<ItemGroup>
<AssemblyAttribute Remove="TemporaryAdditionalHashItem" />
</ItemGroup>
</Target>

Now we recompile whenever there is a change to our added value. For the end result, see here.

UPDATE: In the upcoming SDK at time of writing (2.1.300), this hack is no longer required as @natemcmaster has fixed it here. Hurray!