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.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 @@ public FunctionExecutor(
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 async Task ExecuteWithLoggingAsync(IFunctionInstance instance,
}
}

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
Original file line number Diff line number Diff line change
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; }
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit 77234d6

Please sign in to comment.