_From @jnevins-gcm on July 27, 2018 14:11_
Any way to do this? I know ExecutionContext has the function app base directory, but that鈥檚 too late.
I need to read a config file in the root of the function app from my IExtensionConfigProvider
Thanks
_Copied from original issue: Azure/Azure-Functions#899_
Yes, I am wondering the same. I need the FunctionAppDirectoryfrom the ExecutionContext while using my own binding.
been a while, any possibility of an answer from the team?
With our new extension-loading mechanism in functions v2, you can use constructor injection to ask for any services that the JobHost has loaded. For this, you should be able to ask for an IOptions<> that would have those details. Can you share your scenarios with us here? Then I can put together an example for you.
This change is still very new (not yet released) so there's no documentation yet.
that would be wonderful, thanks! Here is the current code we have - it's just trying to load an nlog.config deployed with our app.
public class NLogConfiguration : IExtensionConfigProvider
{
static NLogConfiguration()
{
Target.Register<BufferedSumoLogicTarget>(nameof(BufferedSumoLogicTarget));
LogManager.ThrowConfigExceptions = true;
// Until the webjobs team can suggest another way, this seems to be the best bet for getting the config file path from this context
var configPath = Environment.ExpandEnvironmentVariables(@"%HOME%/site/wwwroot/nlog.config");
// nlog breaks when using a relative path because it looks for AppDomain BaseDirectory which is not the current directory when running from an Azure Function (but the Environment.CurrentDirectory is usable)
if (!File.Exists(configPath)) configPath = Path.GetFullPath("nlog.config");
LogManager.LoadConfiguration(configPath);
}
public void Initialize(ExtensionConfigContext context)
{
// So Azure Functions log messages (not from us) also get to NLog.
context.Config.LoggerFactory.AddProvider(new NLogLoggerProvider());
}
}
@brettsam: you mentioned being able to suggest an approach?
Here's an example -- the root is available on the ExecutionContextOptions:
public class Sample : IExtensionConfigProvider
{
private readonly string _appRoot;
public Sample(IOptions<ExecutionContextOptions> options)
{
_appRoot = options.Value.AppDirectory;
}
public void Initialize(ExtensionConfigContext context)
{
}
}
Yep figured that out over the weekend too. Thanks
Is there any possibility to get the ExecutionContext's FunctionAppDirectory without retrieving it via a function's parameter or via Options<>? Maybe by an environment variable or the like?
Is there a scenario where you need the app directory but can't use either of these two methods for finding it? We try not to write out environment variables unless we determine there's no other easy way to handle a scenario.
I use dependency injection to inject services via an [Inject] attribute to inject dependencies into the function directly. I need some kind of configuration reader that extracts information either locally or when the functions are deployed to Azure. I do not want to inject configuration directly into the function. When dependencies are injected, the process of reading information takes place before the Azure Function is started. I solved it now in a different way.
This solution is not really satisfying, but works for now.
@holgerleichsenring is there a reason why the IOptions based approach recommended above wouldn't work for you?
@fabiocav
I think that at the moment you want to decide if you use local.settings.json or environment variables, the IOptions are not available.
(At the DI startup-phase I mean...)
I use Willezone.Azure.WebJobs.Extensions.DependencyInjection and the startup-code looks like this:
``` c#
using AzureFunctions.Common;
using Infrastructure.AzureTableStorage.DependencyInjection;
using Infrastructure.AzureTableStorage.Options;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SmartContractAzureFunctionApp;
using SmartContractAzureFunctionApp.Services;
using System;
using SmartContractAzureFunctionApp.Options;
using Willezone.Azure.WebJobs.Extensions.DependencyInjection;
[assembly: WebJobsStartup(typeof(Startup))]
namespace SmartContractAzureFunctionApp
{
internal class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) => builder.AddDependencyInjection
}
internal class ServiceProviderBuilder : IServiceProviderBuilder
{
private readonly ILoggerFactory _loggerFactory;
public ServiceProviderBuilder(ILoggerFactory loggerFactory) => _loggerFactory = loggerFactory;
public IServiceProvider Build()
{
var services = new ServiceCollection();
var builder = new ConfigurationBuilder();
string scriptRoot = AzureFunctionUtils.GetAzureWebJobsScriptRoot();
if (!string.IsNullOrEmpty(scriptRoot))
{
builder.SetBasePath(scriptRoot).AddJsonFile("local.settings.json", optional: false, reloadOnChange: false);
}
builder.AddEnvironmentVariables();
var configuration = builder.Build();
// Important: We need to call CreateFunctionUserCategory, otherwise our log entries might be filtered out.
services.AddSingleton(_ => _loggerFactory.CreateLogger(LogCategories.CreateFunctionUserCategory("Common")));
services.AddSingleton<ISmartContractService, SmartContractService>();
services.AddAzureTableStorage();
// Configure
services.Configure<FunctionAppOptions>(configuration.GetSection("FunctionAppOptions"));
services.Configure<AzureTableStorageOptions>(configuration.GetSection("AzureTableStorageOptions"));
return services.BuildServiceProvider();
}
}
}
```
Most helpful comment
Here's an example -- the root is available on the
ExecutionContextOptions: