Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Guidance require regarding deprecation of IFunctionFilter, IFunctionInvocationFilter & IFunctionExceptionFilter #1934

Open
cResults opened this issue Sep 27, 2018 · 1 comment

Comments

@cResults
Copy link

While attempting to update to Microsoft.Azure.WebJobs 3.0.0 we received the warnings that pretty much everything associated with IFunctionFilter has been deprecated. We do not necessarily need IFunctionFilter, but we do need a means of disposing scope based dependencies.

As you are making improvements to an already amazing product, please keep in mind our use case. If IFunctionFilter does get removed or significantly changed, please advise us here on how to maintain our existing behaviors.

Here is how we are using IFunctionFilter

internal class ScopeCleanupFilter : IFunctionInvocationFilter, IFunctionExceptionFilter
{
    public Task OnExceptionAsync(FunctionExceptionContext exceptionContext, CancellationToken cancellationToken)
    {
        RemoveScope(exceptionContext.FunctionInstanceId);
        return Task.CompletedTask;
    }

    public Task OnExecutedAsync(FunctionExecutedContext executedContext, CancellationToken cancellationToken)
    {
        RemoveScope(executedContext.FunctionInstanceId);
        return Task.CompletedTask;
    }

    public Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken) =>
        Task.CompletedTask;

    private void RemoveScope(Guid id)
    {
        if (IoC.Scopes.TryRemove(id, out var scope))
        {
            scope.Dispose();
        }
    }
}

Here's the associated IBinding implementation that creates the IServiceScope:

internal class InjectBinding : IBinding
{
    private readonly Type _type;
    private readonly IServiceProvider _serviceProvider;

    internal InjectBinding(IServiceProvider serviceProvider, Type type)
    {
        _type = type;
        _serviceProvider = serviceProvider;
    }

    public bool FromAttribute => true;

    public Task<IValueProvider> BindAsync(object value, ValueBindingContext context) =>
        Task.FromResult((IValueProvider)new InjectValueProvider(value));

    public async Task<IValueProvider> BindAsync(BindingContext context)
    {
        await Task.Yield();
        var scope = IoC.Scopes.GetOrAdd(context.FunctionInstanceId, (_) => _serviceProvider.CreateScope());
        var value = scope.ServiceProvider.GetRequiredService(_type);
        return await BindAsync(value, context.ValueContext);
    }

    public ParameterDescriptor ToParameterDescriptor() => new ParameterDescriptor();

    private class InjectValueProvider : IValueProvider
    {
        private readonly object _value;

        public InjectValueProvider(object value) => _value = value;

        public Type Type => _value.GetType();

        public Task<object> GetValueAsync() => Task.FromResult(_value);

        public string ToInvokeString() => _value.ToString();
    }
}
@mlankamp
Copy link

Be carefull with using this pattern in functions V2. When the functions is under heavy load you could receive an error 500 because of a non thread safe ScopedResolver (see Azure/azure-functions-host#3494)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants