aspnet / Configuration

[Archived] Interfaces and providers for accessing configuration files. Project moved to https://github.com/aspnet/Extensions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Microsoft.Extensions.Configuration.UserSecrets.targets injects source file as last in project to compile

vdehaven opened this issue · comments

Title

Microsoft.Extensions.Configuration.UserSecrets.targets injects source file as last in project to compile

Functional impact

This ordering precludes the package's use in F# dotnet console applications.

Minimal repro steps

  1. Create an F# console app. dotnet new console --language F#
  2. Add Microsoft.Extensions.SecretManager.Tools package. dotnet add package Microsoft.Extensions.SecretManager.Tools
  3. Add a user secrets ID and the tools reference for secret creation.
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <UserSecretsId>aGUIDmaybe</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.2" />
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.2" />
  </ItemGroup>

</Project>
  1. Add a secret. dotnet user-secrets set MySecret ThisIsMySecret
  2. Replace Program.fs with the following:
module Program

open Microsoft.Extensions.Configuration
open System

[<EntryPoint>]
let main _ =
    let builder = ((new ConfigurationBuilder())
                      .SetBasePath Environment.CurrentDirectory)
                      .AddUserSecrets()
    let config = builder.Build()

    printfn "Here's your secret setting: %s" config.["MySecret"]
    0

  1. Restore and build dotnet restore; dotnet build --verbosity detailed

Expected result

The generated UserSecretsAssemblyInfo.fs should be compiled prior to the last file, e.g., the file with the [<EntryPoint>] attribute.

Actual result

UserSecretsAssemblyInfo.fs is compiled last, as seen in the verbose build output.

C:\Users\username\AppData\Local\Temp.NETCoreApp,Version=v2.0.AssemblyAttributes.fs
Program.fs
obj\Debug\netcoreapp2.0\UserSecretsAssemblyInfo.fs

This results in:

error FS0433: A function labeled with the 'EntryPointAttribute' attribute must be the last declaration in the last file in the compilation sequence.

Further technical details

  • Windows 10.0.16299
  • .NET SDK 2.1.105
  • Microsoft.Extensions.SecretManager.Tools 2.0.2

Attempting to circumvent the compile order by adding the generated assembly info file via
<Compile Include="**/UserSecretsAssemblyInfo.fs" /> is foiled by the UserSecrets.targets file:

    <ItemGroup>
      <!--Ensure generated file is not already in compile sources-->
      <Compile Remove="$(GeneratedUserSecretsAttributeFile)"  />
    </ItemGroup>

A work-around

Remove the <UserSecretsId> element from the .fsproj. Instead, load the user secrets ID from an appsettings file, such as:

{
    "userSecretsId": "aGUIDmaybe"
}

using:

[<EntryPoint>]
let main _ =
    printfn "Loading configuration..."
    let builder = ((new ConfigurationBuilder())
                      .SetBasePath Environment.CurrentDirectory)
                      .AddJsonFile "Properties/appsettings.json"
    let intermediate = builder.Build() // Intermediate build to get the user secrets.
    let builder = builder.AddUserSecrets(intermediate.["userSecretsId"])
    let config = builder.Build()

    printfn "Here's your secret setting: %s" config.["MySecret"]
    0

Ran into this issue with latest SDK and packages, so still not solved.
@vdehaven Thanks for the workaround!

Resolved with #872.