Skip to content

Commit

Permalink
Adding support for scoped service injection
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiocav committed Mar 5, 2019
1 parent f28c80d commit 77234d6
Show file tree
Hide file tree
Showing 27 changed files with 476 additions and 187 deletions.
Expand Up @@ -5,23 +5,25 @@

namespace Microsoft.Azure.WebJobs.Host.Executors
{
internal class ActivatorInstanceFactory<TReflected> : IFactory<TReflected>
internal class ActivatorInstanceFactory<T> : IJobInstanceFactory<T>
{
private readonly IJobActivator _activator;
private readonly Func<IFunctionInstanceEx, T> _createInstance;

public ActivatorInstanceFactory(IJobActivator activator)
{
if (activator == null)
{
throw new ArgumentNullException("activator");
throw new ArgumentNullException(nameof(activator));
}

_activator = activator;
_createInstance = activator is IJobActivatorEx activatorEx
? new Func<IFunctionInstanceEx, T>(i => activatorEx.CreateInstance<T>(i))
: new Func<IFunctionInstanceEx, T>(i => activator.CreateInstance<T>());
}

public TReflected Create()
public T Create(IFunctionInstanceEx functionInstance)
{
return _activator.CreateInstance<TReflected>();
return _createInstance(functionInstance);
}
}
}
14 changes: 12 additions & 2 deletions src/Microsoft.Azure.WebJobs.Host/Executors/DefaultJobActivator.cs
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
/// The default <see cref="IJobActivator"/> integrates with DI,
/// supporting constructor injection for registered services.
/// </summary>
internal class DefaultJobActivator : IJobActivator
internal class DefaultJobActivator : IJobActivatorEx
{
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<Type, ObjectFactory> _factories;
Expand All @@ -23,13 +23,23 @@ public DefaultJobActivator(IServiceProvider serviceProvider)
}

public T CreateInstance<T>()
{
return CreateInstance<T>(_serviceProvider);
}

public T CreateInstance<T>(IFunctionInstanceEx functionInstance)
{
return CreateInstance<T>(functionInstance.InstanceServices);
}

private T CreateInstance<T>(IServiceProvider serviceProvider)
{
var factory = _factories.GetOrAdd(typeof(T), t =>
{
return ActivatorUtilities.CreateFactory(t, Type.EmptyTypes);
});

return (T)factory(_serviceProvider, null);
return (T)factory(serviceProvider, null);
}
}
}
57 changes: 45 additions & 12 deletions src/Microsoft.Azure.WebJobs.Host/Executors/FunctionExecutor.cs
Expand Up @@ -17,6 +17,7 @@
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Microsoft.Azure.WebJobs.Host.Executors
Expand All @@ -26,6 +27,7 @@ internal class FunctionExecutor : IFunctionExecutor
private readonly IFunctionInstanceLogger _functionInstanceLogger;
private readonly IWebJobsExceptionHandler _exceptionHandler;
private readonly IAsyncCollector<FunctionInstanceLogEntry> _functionEventCollector;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger _resultsLogger;
private readonly IEnumerable<IFunctionFilter> _globalFunctionFilters;
Expand All @@ -50,13 +52,15 @@ internal class FunctionExecutor : IFunctionExecutor
IFunctionOutputLogger functionOutputLogger,
IWebJobsExceptionHandler exceptionHandler,
IAsyncCollector<FunctionInstanceLogEntry> functionEventCollector,
IServiceScopeFactory serviceScopeFactory,
ILoggerFactory loggerFactory = null,
IEnumerable<IFunctionFilter> globalFunctionFilters = null)
{
_functionInstanceLogger = functionInstanceLogger ?? throw new ArgumentNullException(nameof(functionInstanceLogger));
_functionOutputLogger = functionOutputLogger;
_exceptionHandler = exceptionHandler ?? throw new ArgumentNullException(nameof(exceptionHandler));
_functionEventCollector = functionEventCollector ?? throw new ArgumentNullException(nameof(functionEventCollector));
_serviceScopeFactory = serviceScopeFactory;
_loggerFactory = loggerFactory;
_resultsLogger = _loggerFactory?.CreateLogger(LogCategories.Results);
_globalFunctionFilters = globalFunctionFilters ?? Enumerable.Empty<IFunctionFilter>();
Expand All @@ -68,8 +72,32 @@ public HostOutputMessage HostOutputMessage
set { _hostOutputMessage = value; }
}

public async Task<IDelayedException> TryExecuteAsync(IFunctionInstance functionInstance, CancellationToken cancellationToken)
public async Task<IDelayedException> TryExecuteAsync(IFunctionInstance instance, CancellationToken cancellationToken)
{
bool ownsInstance = false;

if (!(instance is IFunctionInstanceEx functionInstance))
{
functionInstance = new FunctionInstanceWrapper(instance, _serviceScopeFactory);
ownsInstance = true;
}

try
{
return await TryExecuteAsyncCore(functionInstance, cancellationToken);
}
finally
{
if (ownsInstance)
{
(functionInstance as IDisposable)?.Dispose();
}
}
}

private async Task<IDelayedException> TryExecuteAsyncCore(IFunctionInstanceEx functionInstance, CancellationToken cancellationToken)
{

ILogger logger = _loggerFactory?.CreateLogger(LogCategories.CreateFunctionCategory(functionInstance.FunctionDescriptor.LogName));

FunctionStartedMessage functionStartedMessage = CreateStartedMessageWithoutArguments(functionInstance);
Expand Down Expand Up @@ -196,7 +224,7 @@ internal static async Task HandleExceptionAsync(TimeoutAttribute timeout, Except
}
}

private async Task<string> ExecuteWithLoggingAsync(IFunctionInstance instance, FunctionStartedMessage message,
private async Task<string> ExecuteWithLoggingAsync(IFunctionInstanceEx instance, FunctionStartedMessage message,
FunctionInstanceLogEntry instanceLogEntry, ParameterHelper parameterHelper, ILogger logger, CancellationToken cancellationToken)
{
IFunctionOutputDefinition outputDefinition = null;
Expand Down Expand Up @@ -420,7 +448,7 @@ private static ITaskSeriesTimer StartParameterLogTimer(IRecurrentCommand updateC
return timer;
}

private async Task ExecuteWithLoggingAsync(IFunctionInstance instance,
private async Task ExecuteWithLoggingAsync(IFunctionInstanceEx instance,
ParameterHelper parameterHelper,
IFunctionOutputDefinition outputDefinition,
ILogger logger,
Expand Down Expand Up @@ -458,12 +486,12 @@ private static ITaskSeriesTimer StartParameterLogTimer(IRecurrentCommand updateC
}
}

internal async Task ExecuteWithWatchersAsync(IFunctionInstance instance,
internal async Task ExecuteWithWatchersAsync(IFunctionInstanceEx instance,
ParameterHelper parameterHelper,
ILogger logger,
CancellationTokenSource functionCancellationTokenSource)
{
IFunctionInvoker invoker = instance.Invoker;
IFunctionInvokerEx invoker = instance.GetFunctionInvoker();
IDelayedException delayedBindingException = await parameterHelper.PrepareParametersAsync();

if (delayedBindingException != null)
Expand Down Expand Up @@ -760,7 +788,7 @@ private Task NotifyCompleteAsync(FunctionInstanceLogEntry instanceLogEntry, IDic
// 3. System.Object[]. which can be passed to the actual MethodInfo for execution
internal class ParameterHelper : IDisposable
{
private readonly IFunctionInstance _functionInstance;
private readonly IFunctionInstanceEx _functionInstance;

// Logs, contain the result from invoking the IWatchers.
private IDictionary<string, ParameterLog> _parameterLogCollector = new Dictionary<string, ParameterLog>();
Expand Down Expand Up @@ -789,7 +817,7 @@ public ParameterHelper()
{
}

public ParameterHelper(IFunctionInstance functionInstance)
public ParameterHelper(IFunctionInstanceEx functionInstance)
{
if (functionInstance == null)
{
Expand All @@ -814,7 +842,7 @@ public ParameterHelper(IFunctionInstance functionInstance)

public void Initialize()
{
JobInstance = _functionInstance.Invoker.CreateInstance();
JobInstance = _functionInstance.GetFunctionInvoker().CreateInstance(_functionInstance);
}

// Convert the parameters and their names to a dictionary
Expand Down Expand Up @@ -1083,17 +1111,17 @@ private static BindStepOrder GetStepOrder(IValueProvider provider)
}
}

private class FunctionInvocationFilterInvoker : IFunctionInvoker
private class FunctionInvocationFilterInvoker : IFunctionInvokerEx
{
private List<IFunctionInvocationFilter> _filters;
private IFunctionInvoker _innerInvoker;
private IFunctionInvokerEx _innerInvoker;
private IFunctionInstance _functionInstance;
private ParameterHelper _parameterHelper;
private ILogger _logger;

public IReadOnlyList<string> ParameterNames => _innerInvoker.ParameterNames;

public static IFunctionInvoker Create(IFunctionInvoker innerInvoker, List<IFunctionInvocationFilter> filters, IFunctionInstance functionInstance, ParameterHelper parameterHelper, ILogger logger)
public static IFunctionInvokerEx Create(IFunctionInvokerEx innerInvoker, List<IFunctionInvocationFilter> filters, IFunctionInstance functionInstance, ParameterHelper parameterHelper, ILogger logger)
{
if (filters.Count == 0)
{
Expand All @@ -1112,7 +1140,12 @@ public static IFunctionInvoker Create(IFunctionInvoker innerInvoker, List<IFunct

public object CreateInstance()
{
return _innerInvoker.CreateInstance();
throw new NotSupportedException($"{nameof(CreateInstance)} is not supported. Please use the overload that accepts an instance of an {nameof(IFunctionInstance)}");
}

public object CreateInstance(IFunctionInstanceEx functionInstance)
{
return _innerInvoker.CreateInstance(functionInstance);
}

public async Task<object> InvokeAsync(object instance, object[] arguments)
Expand Down
57 changes: 14 additions & 43 deletions src/Microsoft.Azure.WebJobs.Host/Executors/FunctionInstance.cs
Expand Up @@ -9,59 +9,30 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
{
internal class FunctionInstance : IFunctionInstance
{
private readonly Guid _id;
private readonly IDictionary<string, string> _triggerDetails;
private readonly Guid? _parentId;
private readonly ExecutionReason _reason;
private readonly IBindingSource _bindingSource;
private readonly IFunctionInvoker _invoker;
private readonly FunctionDescriptor _functionDescriptor;

public FunctionInstance(Guid id, IDictionary<string, string> triggerDetails, Guid? parentId, ExecutionReason reason, IBindingSource bindingSource,
IFunctionInvoker invoker, FunctionDescriptor functionDescriptor)
{
_id = id;
_triggerDetails = triggerDetails;
_parentId = parentId;
_reason = reason;
_bindingSource = bindingSource;
_invoker = invoker;
_functionDescriptor = functionDescriptor;
Id = id;
TriggerDetails = triggerDetails;
ParentId = parentId;
Reason = reason;
BindingSource = bindingSource;
Invoker = invoker;
FunctionDescriptor = functionDescriptor;
}

public Guid Id
{
get { return _id; }
}
public Guid Id { get; }

public IDictionary<string, string> TriggerDetails
{
get { return _triggerDetails; }
}
public IDictionary<string, string> TriggerDetails { get; }

public Guid? ParentId
{
get { return _parentId; }
}
public Guid? ParentId { get; }

public ExecutionReason Reason
{
get { return _reason; }
}
public ExecutionReason Reason { get; }

public IBindingSource BindingSource
{
get { return _bindingSource; }
}
public IBindingSource BindingSource { get; }

public IFunctionInvoker Invoker
{
get { return _invoker; }
}
public IFunctionInvoker Invoker { get; }

public FunctionDescriptor FunctionDescriptor
{
get { return _functionDescriptor; }
}
public FunctionDescriptor FunctionDescriptor { get; }
}
}
@@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Azure.WebJobs.Host.Executors
{
public static class FunctionInstanceExtensions
{
public static IServiceProvider GetInstanceServices(this IFunctionInstance instance)
{
if (instance is IFunctionInstanceEx functionInstance)
{
return functionInstance.InstanceServices;
}

return null;
}

internal static IFunctionInvokerEx GetFunctionInvoker(this IFunctionInstance instance)
{
if (instance.Invoker == null)
{
return null;
}

if (instance.Invoker is IFunctionInvokerEx invoker)
{
return invoker;
}


return new FunctionInvokerWrapper(instance.Invoker);
}
}
}

0 comments on commit 77234d6

Please sign in to comment.