Můj oblíbený systém automatického verzování podle datumu a času není kompatibilní s novými funkcemi .NETu pro snazší vývoj, jako je hot reload a částečná kompilace. To přestane fungovat pokud se při buildu změní verze assembly. Proto jsem musel přijít s jiným způsobem, jak zjistit datum a čas buildu aplikace.

Assembly metadata

Po vydání první verze článku (která popisuje automatické generování resource souboru) jsem byl uživatelem harrison314_sk na Twitteru upozorněn, že existuje jednodušší způsob, jak do assembly vložit datum buildu (a v podstatě jakákoliv jiná metadata).

Stačí do .csproj souboru vložit element AssemblyMetadata, kterým můžete přidat do assembly vlastní informace stylem klíč-hodnota. Zde vkládám datum buildu a název počítače, na kterém byl build proveden:

<ItemGroup>
    <AssemblyMetadata Include="BuildDate" Value="$([System.DateTime]::UtcNow.ToString('s'))" />
    <AssemblyMetadata Include="BuildComputer" Value="$(ComputerName)" />
</ItemGroup>

Popis použité syntaxe a možností najdete v článku MSBuild properties na docs.microsoft.com.

Hodnotu pak můžete načíst přes reflexi. Zde jsou dvě funkce, které načtou a vrátí shora uvedené vlastnosti:

private static DateTime GetAssemblyBuildDate(Assembly? assembly) {
    if (assembly == null) return DateTime.MinValue;
    var attrs = assembly.GetCustomAttributes<AssemblyMetadataAttribute>();
    var dateString = attrs.FirstOrDefault(x => x.Key.Equals("BuildDate"))?.Value;
    var parseResult = DateTime.TryParseExact(dateString, "s", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dt);
    return parseResult ? dt : DateTime.MinValue;
}

private static string? GetAssemblyBuildComputer(Assembly? assembly) {
    if (assembly == null) return null;
    var attrs = assembly.GetCustomAttributes<AssemblyMetadataAttribute>();
    return attrs.FirstOrDefault(x => x.Key.Equals("BuildComputer"))?.Value;
}

Automatické generování do resources

Jako většina ostatních platforem, i .NET zná koncept resources. Tedy ukládání dat jako jsou řetězce, obrázky, ikony a obecná data mimo zdrojový kód tak, aby byla z tohoto kódu snadno dostupná a snadno lokalizovatelná. Tento koncept využívá moje původní metoda pro automatické generování času buildu.

Nejdříve si zobrazte vlastnosti projektu. V sekci Build zvolte Events a do pole Pre-build event zadejte následující příkaz:

powershell -Command "((Get-Date).ToUniversalTime()).ToString(\"s\") | Out-File '$(ProjectDir)Properties\BuildDate.txt'"

Screenshot

Můžete také přímo editovat .csproj soubor a zadat do elementu <Project> následující kód:

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="powershell -Command &quot;((Get-Date).ToUniversalTime()).ToString(\&quot;s\&quot;) | Out-File '$(ProjectDir)Properties\BuildDate.txt'&quot;" />
</Target>

Poté v sekci Resources klepněte na Create or open assembly resources.

Screenshot

Tím vytvoříte reource ./Properties/Resources.resx. Můžete ho samozřejmě vytvořit i ručně. Pokud chcete aby byla hodnota viditelná i mimo assembly, nastavte Access Modifier na Public:

Screenshot

Nyní aplikaci zkompilujte. Před kompilací bude vytvořen soubor ./Properties/BuildDate.txt, který bude obsahovat aktuální datum a čas (UTC) ve formátu yyyy-MM-ddTHH:mm:ss.

Nyní tento soubor ze Solution Exploreru přetáhněte myší do okna, kde je zobrazen Resources.resx. Tím se vytvoří nový klíč, jehož obsahem bude obsah souboru:

Screenshot

Obsah tohoto souboru se mění s každým buildem, takže když se zeptáte na hodnotu Properties.Resources.BuildDate, dostanete vždy řetězec obsahující datum a čas, kdy byla aplikace zkompilována.

Pokud používáte Git, přidejte soubor BuildDate.txt do .gitignore, ať vám nedělá zmatek v repozitáři.