Adding AssemblyMetadataAttribute using new SDK project, with MSBuild
Tags: msbuild, dotnetcore
Published: 2019-03-10 19:00:21

---
title: Adding AssemblyMetadataAttribute using new SDK project, with MSBuild
published: true
tags: msbuild,dotnetcore
canonical_url: https://stu.dev/adding-assemblymetadataattribute-using-new-sdk-project-with-msbuild/
---

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](https://github.com/aspnet/EntityFrameworkCore/blob/cfed64d50977fc7c85267ddaab0aa96d834776cd/src/EFCore/Properties/InternalsVisibleTo.cs).

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:

```xml
<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:

```xml
<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`](https://github.com/dotnet/sdk/blob/v2.1.4/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.GenerateAssemblyInfo.targets#L98-L100).

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

```xml
<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](https://github.com/justeat/httpclient-interception/blob/2a7cca38244435ec4065b2030748a4a876759e2d/Directory.Build.targets#L9-L48).

UPDATE: In the upcoming SDK at the time of writing (2.1.300), this hack is no longer required as @natemcmaster [has fixed it here](https://github.com/dotnet/sdk/commit/73fb22a07091585380ead0bcbcdc38839a0266e8#diff-5b286bd8dee67543f08738e5605f0d3fR99). Hurray!