Skip to content

Commit 7a08e12

Browse files
junalmeidawaynebrantley
andauthoredMay 18, 2021
Use correct interfaces to build host (#17)
* Do not overwrite existing IConfiguration * Adding documentation and syntax changes **Possible breaking changes:** 1. Please read sample `Startup.cs` to see how to use `UseAppSettings`. 2. This package will now use AZURE_FUNCTIONS_ENVIRONMENT to determine your environment. It is `Production` by default on Azure servers, `Development` locally. Co-authored-by: Wayne Brantley <wayne@miniter.com>
1 parent e76a929 commit 7a08e12

File tree

8 files changed

+97
-63
lines changed

8 files changed

+97
-63
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
22
using Microsoft.Extensions.Configuration;
3-
using Microsoft.Extensions.DependencyInjection;
4-
using Microsoft.Extensions.DependencyInjection.Extensions;
3+
using Microsoft.Extensions.Hosting;
54
using System;
65
using System.IO;
7-
using System.Reflection;
86

97
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
108
{
@@ -14,57 +12,53 @@ namespace Autofac.Extensions.DependencyInjection.AzureFunctions
1412
public static class AppSettingsExtensions
1513
{
1614
/// <summary>
17-
/// Creates an <see cref="IConfiguration"/> instance and attaches to an `appsettings.json` file, also adding an `appsettings.{environment}.json` on top of it, if available, based on current ASPNETCORE_ENVIRONMENT environment variable.
15+
/// Creates an <see cref="IConfiguration"/> instance and attaches to an `appsettings.json` file, also adding an `appsettings.{environment}.json` on top of it, if available, based on current AZURE_FUNCTIONS_ENVIRONMENT environment variable.
1816
/// </summary>
19-
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
20-
/// <returns>The IFunctionsHostBuilder.</returns>
21-
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder)
17+
/// <param name="builder">An instance of <see cref="IFunctionsConfigurationBuilder"/>.</param>
18+
/// <param name="reloadOnChange">Whether the configuration should be reloaded on file change.</param>
19+
/// <returns>The <see cref="IFunctionsConfigurationBuilder"/>.</returns>
20+
public static IFunctionsConfigurationBuilder UseAppSettings(this IFunctionsConfigurationBuilder builder, bool reloadOnChange = false)
2221
{
23-
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
24-
string currentDirectory = fileInfo.Directory.Parent.FullName;
22+
var context = builder.GetContext();
23+
builder.ConfigurationBuilder
24+
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: reloadOnChange)
25+
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{builder.GetCurrentEnvironmentName()}.json"), optional: true, reloadOnChange: reloadOnChange)
26+
.AddEnvironmentVariables();
2527

26-
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
27-
if (string.IsNullOrWhiteSpace(environment))
28-
environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
29-
if (string.IsNullOrWhiteSpace(environment))
30-
environment = "Development"; // Fallback to Development when none is set.
31-
32-
return UseAppSettings(hostBuilder, (builder) =>
33-
{
34-
builder
35-
.SetBasePath(currentDirectory)
36-
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
37-
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
38-
.AddJsonFile("host.json", optional: true, reloadOnChange: true)
39-
.AddEnvironmentVariables();
40-
});
28+
return builder;
4129
}
4230

4331
/// <summary>
44-
/// Creates an <see cref="IConfiguration"/> instance and configures it as declared by <paramref name="configurationAction"/> action.
32+
/// Calculate environment from unreliable function environment setting.
33+
/// github.com/Azure/azure-functions-host/issues/6239 problem determining environment.
4534
/// </summary>
46-
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
47-
/// <param name="configurationAction">Action on a <see cref="IConfigurationBuilder"/> that adds configurations to a .NET Core application.</param>
48-
/// <returns>The IFunctionsHostBuilder.</returns>
49-
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder, Action<IConfigurationBuilder> configurationAction = null)
35+
/// <param name="builder"></param>
36+
/// <returns></returns>
37+
public static string GetCurrentEnvironmentName(this IFunctionsHostBuilder builder)
5038
{
51-
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;
52-
53-
using (var temporaryServiceProvider = hostBuilder.Services.BuildServiceProvider())
54-
{
55-
var configRoot = temporaryServiceProvider.GetService<IConfiguration>();
56-
if (configRoot != null)
57-
{
58-
configurationBuilder.AddConfiguration(configRoot);
59-
}
60-
}
39+
return GetCurrentEnvironmentName(builder.GetContext().EnvironmentName);
40+
}
6141

62-
configurationAction?.Invoke(configurationBuilder);
63-
var configuration = configurationBuilder.Build();
42+
/// <summary>
43+
/// Calculate environment from unreliable function environment setting.
44+
/// github.com/Azure/azure-functions-host/issues/6239 problem determining environment.
45+
/// </summary>
46+
/// <param name="builder"></param>
47+
/// <returns></returns>
48+
public static string GetCurrentEnvironmentName(this IFunctionsConfigurationBuilder builder)
49+
{
50+
return GetCurrentEnvironmentName(builder.GetContext().EnvironmentName);
51+
}
6452

65-
hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));
53+
private static string GetCurrentEnvironmentName(string contextEnvironmentName)
54+
{
6655

67-
return hostBuilder;
56+
string environmentName = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ??
57+
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ??
58+
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ??
59+
contextEnvironmentName ??
60+
EnvironmentName.Development;
61+
return environmentName;
6862
}
6963
}
7064
}

‎Autofac.Extensions.DependencyInjection.AzureFunctions/LoggerExtensions.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Microsoft.Extensions.DependencyInjection;
44
using Microsoft.Extensions.Logging;
55
using System;
6-
using System.Linq;
76

87
namespace Autofac.Extensions.DependencyInjection.AzureFunctions
98
{
@@ -20,10 +19,9 @@ public static class LoggerExtensions
2019
/// <returns>The IFunctionsHostBuilder.</returns>
2120
public static IFunctionsHostBuilder UseLogger(this IFunctionsHostBuilder hostBuilder, Action<ILoggingBuilder, IConfiguration> configure)
2221
{
23-
var configuration = hostBuilder.Services.Where(x => x.ServiceType == typeof(IConfiguration)).SingleOrDefault()?.ImplementationInstance as IConfiguration;
22+
var configuration = hostBuilder.GetContext().Configuration;
2423

2524
hostBuilder.Services.AddLogging((config) => configure?.Invoke(config, configuration));
26-
2725
return hostBuilder;
2826
}
2927
}

‎Autofac.Extensions.DependencyInjection.AzureFunctions/LoggerModule.cs

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ internal class LoggerModule : Module
66
{
77
public const string functionNameParam = "functionName";
88
public const string loggerFactoryParam = "loggerFactory";
9-
public const string telemetryParam = "telemetry";
109

1110
protected override void Load(ContainerBuilder builder)
1211
{

‎README.md

+21-5
Original file line numberDiff line numberDiff line change
@@ -34,27 +34,43 @@ public class Startup : FunctionsStartup
3434
public override void Configure(IFunctionsHostBuilder builder)
3535
{
3636
builder
37-
.UseAppSettings() // this is optional, this will bind IConfiguration in the container.
37+
// This is the required call in order to use autofac in your azure functions app
3838
.UseAutofacServiceProviderFactory(ConfigureContainer);
3939
}
4040

41+
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
42+
{
43+
// this is optional and will bind IConfiguration with appsettings.json in
44+
// the container, like it is usually done in regular dotnet console and
45+
// web applications.
46+
builder.UseAppSettings();
47+
}
48+
4149
private void ConfigureContainer(ContainerBuilder builder)
4250
{
4351
builder
4452
.Register(activator =>
4553
{
4654
// Example on how to bind settings from appsettings.json
4755
// to a class instance
48-
var mySettings = new MySettings();
56+
var section = activator.Resolve<IConfiguration>().GetSection(nameof(MySettings));
4957

50-
var config = activator.Resolve<IConfiguration>();
51-
config.GetSection(nameof(MySettings)).Bind(mySettings);
58+
var instance = section.Get<MySettings>();
5259

53-
return mySettings;
60+
// If you expect IConfiguration to change (with reloadOnChange=true), use
61+
// token to rebind.
62+
ChangeToken.OnChange(
63+
() => section.GetReloadToken(),
64+
(state) => section.Bind(state),
65+
instance);
66+
67+
return instance;
5468
})
5569
.AsSelf()
5670
.SingleInstance();
5771

72+
73+
5874
// Register all functions that resides in a given namespace
5975
// The function class itself will be created using autofac
6076
builder

‎SampleAutofacFunction/SampleAutofacFunction.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
2121
<DependentUpon>appsettings.json</DependentUpon>
2222
</None>
23+
<None Update="appsettings.Development.json">
24+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
25+
<DependentUpon>appsettings.json</DependentUpon>
26+
</None>
2327
<None Update="host.json">
2428
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2529
</None>

‎SampleAutofacFunction/Startup.cs

+27-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
44
using Microsoft.Extensions.Configuration;
55
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.Primitives;
67
using SampleAutofacFunction.Settings;
78

89
[assembly: FunctionsStartup(typeof(SampleAutofacFunction.Startup))]
@@ -14,45 +15,62 @@ class Startup : FunctionsStartup
1415
public override void Configure(IFunctionsHostBuilder builder)
1516
{
1617
builder
17-
.UseAppSettings()
18+
// You can call UseLogger to change how logging is done on your app by code.
1819
.UseLogger(ConfigureLogger)
20+
// This is the required call in order to use autofac in your azure functions app
1921
.UseAutofacServiceProviderFactory(ConfigureContainer);
2022
}
2123

2224
private void ConfigureLogger(ILoggingBuilder builder, IConfiguration config)
2325
{
2426
builder.AddConfiguration(config.GetSection("Logging"));
25-
builder.AddApplicationInsightsWebJobs();
27+
}
28+
29+
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
30+
{
31+
// this is optional and will bind IConfiguration with appsettings.json in
32+
// the container, like it is usually done in regular dotnet console and
33+
// web applications.
34+
builder.UseAppSettings(true);
2635
}
2736

2837
private void ConfigureContainer(ContainerBuilder builder)
2938
{
3039
builder
3140
.Register(activator =>
3241
{
33-
var mySettings = new MySettings();
42+
// Example on how to bind settings from appsettings.json
43+
// to a class instance
44+
var section = activator.Resolve<IConfiguration>().GetSection(nameof(MySettings));
45+
46+
var instance = section.Get<MySettings>();
3447

35-
var config = activator.Resolve<IConfiguration>();
36-
config.GetSection(nameof(MySettings)).Bind(mySettings);
48+
// If you expect IConfiguration to change (with reloadOnChange=true), use
49+
// token to rebind.
50+
ChangeToken.OnChange(
51+
() => section.GetReloadToken(),
52+
(state) => section.Bind(state),
53+
instance);
3754

38-
return mySettings;
55+
return instance;
3956
})
4057
.AsSelf()
4158
.SingleInstance();
4259

60+
// Register all functions that resides in a given namespace
61+
// The function class itself will be created using autofac
4362
builder
4463
.RegisterAssemblyTypes(typeof(Startup).Assembly)
4564
.InNamespace("SampleAutofacFunction.Functions")
46-
.AsSelf()
47-
.InstancePerTriggerRequest();
65+
.AsSelf() // Azure Functions core code resolves a function class by itself.
66+
.InstancePerTriggerRequest(); // This will scope nested dependencies to each function execution
4867

4968
builder
5069
.RegisterAssemblyTypes(typeof(Startup).Assembly)
5170
.InNamespace("SampleAutofacFunction.Services")
5271
.AsImplementedInterfaces()
5372
.InstancePerTriggerRequest();
5473

55-
5674
}
5775
}
5876
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"MySettings": {
3+
"Value1": "Value 1 from appsettings.Development.json",
4+
"Value2": "Value 2 from appsettings.Development.json"
5+
}
6+
}

‎SampleAutofacFunction/local.settings.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"IsEncrypted": false,
33
"Values": {
44
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
5-
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
6-
"ASPNETCORE_ENVIRONMENT": "Production",
5+
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
76
}
87
}

0 commit comments

Comments
 (0)