Skip to content

Use correct interfaces to build host #17

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 4 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using System;
using System.IO;
using System.Reflection;

namespace Autofac.Extensions.DependencyInjection.AzureFunctions
{
Expand All @@ -14,57 +12,53 @@ namespace Autofac.Extensions.DependencyInjection.AzureFunctions
public static class AppSettingsExtensions
{
/// <summary>
/// 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.
/// 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.
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder)
/// <param name="builder">An instance of <see cref="IFunctionsConfigurationBuilder"/>.</param>
/// <param name="reloadOnChange">Whether the configuration should be reloaded on file change.</param>
/// <returns>The <see cref="IFunctionsConfigurationBuilder"/>.</returns>
public static IFunctionsConfigurationBuilder UseAppSettings(this IFunctionsConfigurationBuilder builder, bool reloadOnChange = false)
{
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
string currentDirectory = fileInfo.Directory.Parent.FullName;
var context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: reloadOnChange)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{builder.GetCurrentEnvironmentName()}.json"), optional: true, reloadOnChange: reloadOnChange)
.AddEnvironmentVariables();

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
environment = "Development"; // Fallback to Development when none is set.

return UseAppSettings(hostBuilder, (builder) =>
{
builder
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddJsonFile("host.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
});
return builder;
}

/// <summary>
/// Creates an <see cref="IConfiguration"/> instance and configures it as declared by <paramref name="configurationAction"/> action.
/// Calculate environment from unreliable function environment setting.
/// github.com/Azure/azure-functions-host/issues/6239 problem determining environment.
/// </summary>
/// <param name="hostBuilder">An instance of <see cref="IFunctionsHostBuilder"/>.</param>
/// <param name="configurationAction">Action on a <see cref="IConfigurationBuilder"/> that adds configurations to a .NET Core application.</param>
/// <returns>The IFunctionsHostBuilder.</returns>
public static IFunctionsHostBuilder UseAppSettings(this IFunctionsHostBuilder hostBuilder, Action<IConfigurationBuilder> configurationAction = null)
/// <param name="builder"></param>
/// <returns></returns>
public static string GetCurrentEnvironmentName(this IFunctionsHostBuilder builder)
{
var configurationBuilder = new ConfigurationBuilder() as IConfigurationBuilder;

using (var temporaryServiceProvider = hostBuilder.Services.BuildServiceProvider())
{
var configRoot = temporaryServiceProvider.GetService<IConfiguration>();
if (configRoot != null)
{
configurationBuilder.AddConfiguration(configRoot);
}
}
return GetCurrentEnvironmentName(builder.GetContext().EnvironmentName);
}

configurationAction?.Invoke(configurationBuilder);
var configuration = configurationBuilder.Build();
/// <summary>
/// Calculate environment from unreliable function environment setting.
/// github.com/Azure/azure-functions-host/issues/6239 problem determining environment.
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static string GetCurrentEnvironmentName(this IFunctionsConfigurationBuilder builder)
{
return GetCurrentEnvironmentName(builder.GetContext().EnvironmentName);
}

hostBuilder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));
private static string GetCurrentEnvironmentName(string contextEnvironmentName)
{

return hostBuilder;
string environmentName = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ??
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ??
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ??
contextEnvironmentName ??
EnvironmentName.Development;
return environmentName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;

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

hostBuilder.Services.AddLogging((config) => configure?.Invoke(config, configuration));

return hostBuilder;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ internal class LoggerModule : Module
{
public const string functionNameParam = "functionName";
public const string loggerFactoryParam = "loggerFactory";
public const string telemetryParam = "telemetry";

protected override void Load(ContainerBuilder builder)
{
Expand Down
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,43 @@ public class Startup : FunctionsStartup
public override void Configure(IFunctionsHostBuilder builder)
{
builder
.UseAppSettings() // this is optional, this will bind IConfiguration in the container.
// This is the required call in order to use autofac in your azure functions app
.UseAutofacServiceProviderFactory(ConfigureContainer);
}

public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
// this is optional and will bind IConfiguration with appsettings.json in
// the container, like it is usually done in regular dotnet console and
// web applications.
builder.UseAppSettings();
}

private void ConfigureContainer(ContainerBuilder builder)
{
builder
.Register(activator =>
{
// Example on how to bind settings from appsettings.json
// to a class instance
var mySettings = new MySettings();
var section = activator.Resolve<IConfiguration>().GetSection(nameof(MySettings));

var config = activator.Resolve<IConfiguration>();
config.GetSection(nameof(MySettings)).Bind(mySettings);
var instance = section.Get<MySettings>();

return mySettings;
// If you expect IConfiguration to change (with reloadOnChange=true), use
// token to rebind.
ChangeToken.OnChange(
() => section.GetReloadToken(),
(state) => section.Bind(state),
instance);

return instance;
})
.AsSelf()
.SingleInstance();



// Register all functions that resides in a given namespace
// The function class itself will be created using autofac
builder
Expand Down
4 changes: 4 additions & 0 deletions SampleAutofacFunction/SampleAutofacFunction.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<DependentUpon>appsettings.json</DependentUpon>
</None>
<None Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<DependentUpon>appsettings.json</DependentUpon>
</None>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
36 changes: 27 additions & 9 deletions SampleAutofacFunction/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using SampleAutofacFunction.Settings;

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

private void ConfigureLogger(ILoggingBuilder builder, IConfiguration config)
{
builder.AddConfiguration(config.GetSection("Logging"));
builder.AddApplicationInsightsWebJobs();
}

public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
// this is optional and will bind IConfiguration with appsettings.json in
// the container, like it is usually done in regular dotnet console and
// web applications.
builder.UseAppSettings(true);
}

private void ConfigureContainer(ContainerBuilder builder)
{
builder
.Register(activator =>
{
var mySettings = new MySettings();
// Example on how to bind settings from appsettings.json
// to a class instance
var section = activator.Resolve<IConfiguration>().GetSection(nameof(MySettings));

var instance = section.Get<MySettings>();

var config = activator.Resolve<IConfiguration>();
config.GetSection(nameof(MySettings)).Bind(mySettings);
// If you expect IConfiguration to change (with reloadOnChange=true), use
// token to rebind.
ChangeToken.OnChange(
() => section.GetReloadToken(),
(state) => section.Bind(state),
instance);

return mySettings;
return instance;
})
.AsSelf()
.SingleInstance();

// Register all functions that resides in a given namespace
// The function class itself will be created using autofac
builder
.RegisterAssemblyTypes(typeof(Startup).Assembly)
.InNamespace("SampleAutofacFunction.Functions")
.AsSelf()
.InstancePerTriggerRequest();
.AsSelf() // Azure Functions core code resolves a function class by itself.
.InstancePerTriggerRequest(); // This will scope nested dependencies to each function execution

builder
.RegisterAssemblyTypes(typeof(Startup).Assembly)
.InNamespace("SampleAutofacFunction.Services")
.AsImplementedInterfaces()
.InstancePerTriggerRequest();


}
}
}
6 changes: 6 additions & 0 deletions SampleAutofacFunction/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"MySettings": {
"Value1": "Value 1 from appsettings.Development.json",
"Value2": "Value 2 from appsettings.Development.json"
}
}
3 changes: 1 addition & 2 deletions SampleAutofacFunction/local.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"ASPNETCORE_ENVIRONMENT": "Production",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}