├── .gitattributes ├── .gitignore ├── Azure.Functions.Dependency.Injection.sln ├── FuncInjector ├── .gitignore ├── FuncInjector.csproj ├── Injection │ ├── InjectAttribute.cs │ ├── InjectBinding.cs │ └── InjectBindingProvider.cs └── RegisterServicesTrigger │ ├── RegisterServicesTrigger.cs │ ├── RegisterServicesTriggerAttribute.cs │ ├── RegisterServicesTriggerBinding.cs │ ├── RegisterServicesTriggerBindingProvider.cs │ ├── RegisterServicesTriggerListener.cs │ └── ReleaseScopedServicesFilter.cs ├── FunctionProject ├── .gitignore ├── FunctionProject.csproj ├── GreeterFunction.cs ├── Properties │ └── PublishProfiles │ │ └── mv10blog - Web Deploy.pubxml └── host.json ├── LICENSE ├── Library ├── .gitignore ├── GreeterService │ ├── AGreeter.cs │ ├── CountUpGreeter.cs │ ├── Counter.cs │ ├── GreeterInjectionExtensions.cs │ ├── ICounter.cs │ └── IGreeter.cs ├── Library.csproj └── ScopedService │ ├── GreeterConsumer.cs │ ├── GreeterConsumerInjectionExtensions.cs │ └── IGreeterConsumer.cs └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin 3 | obj -------------------------------------------------------------------------------- /Azure.Functions.Dependency.Injection.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FuncInjector", "FuncInjector\FuncInjector.csproj", "{DB5F3085-923D-4E29-99C6-0C7D41344ED8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionProject", "FunctionProject\FunctionProject.csproj", "{E8649CD1-8192-4477-AF91-C96943DA0154}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library", "Library\Library.csproj", "{ED8FCE60-0C06-4BBD-AE33-CE53444AE964}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {DB5F3085-923D-4E29-99C6-0C7D41344ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {DB5F3085-923D-4E29-99C6-0C7D41344ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {DB5F3085-923D-4E29-99C6-0C7D41344ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {DB5F3085-923D-4E29-99C6-0C7D41344ED8}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {E8649CD1-8192-4477-AF91-C96943DA0154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {E8649CD1-8192-4477-AF91-C96943DA0154}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {E8649CD1-8192-4477-AF91-C96943DA0154}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {E8649CD1-8192-4477-AF91-C96943DA0154}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {ED8FCE60-0C06-4BBD-AE33-CE53444AE964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {ED8FCE60-0C06-4BBD-AE33-CE53444AE964}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {ED8FCE60-0C06-4BBD-AE33-CE53444AE964}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {ED8FCE60-0C06-4BBD-AE33-CE53444AE964}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {9DE6AA97-0CD9-479E-A9C2-9112BE8D8FC7} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /FuncInjector/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /FuncInjector/FuncInjector.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net461 4 | true 5 | 1.0.0.0 6 | 7 | 8 | latest 9 | 10 | 11 | latest 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /FuncInjector/Injection/InjectAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | 4 | namespace FuncInjector 5 | { 6 | /// 7 | /// Declares a dependency on an interface to be resolved at runtime. The name of a 8 | /// RegisterServicesTrigger is optional. If omitted, a service registration trigger 9 | /// named "RegisterServices" must exist. 10 | /// 11 | [Binding] 12 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] 13 | public class InjectAttribute : Attribute 14 | { 15 | public string RegisterServicesFunctionName { get; private set; } 16 | 17 | public InjectAttribute(string configFunctionName = "RegisterServices") 18 | { 19 | RegisterServicesFunctionName = configFunctionName; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /FuncInjector/Injection/InjectBinding.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Host.Bindings; 2 | using Microsoft.Azure.WebJobs.Host.Protocols; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | 8 | namespace FuncInjector 9 | { 10 | /// 11 | /// Tracks the Type declared as a dependency and the RegisterServices trigger which 12 | /// prepares the dependency graph for injection. The trigger is stored as the value-provider, 13 | /// so resolving the dependency causes trigger execution. 14 | /// 15 | public class InjectBinding : IBinding 16 | { 17 | private readonly bool isArray =false; 18 | private readonly bool hasDefaultValue = false; 19 | private readonly object defaultValue; 20 | 21 | private readonly Type elementType; 22 | private readonly RegisterServicesTrigger triggerAttribute; 23 | private readonly string triggerFunction; 24 | 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | public InjectBinding(ParameterInfo parameterInfo, RegisterServicesTrigger regSvcTrigger, string regSvcFunctionName) 31 | { 32 | this.hasDefaultValue = parameterInfo.HasDefaultValue; 33 | this.defaultValue = parameterInfo.DefaultValue; 34 | 35 | this.isArray = parameterInfo.ParameterType.IsArray; 36 | this.elementType = parameterInfo.ParameterType.IsArray ? 37 | parameterInfo.ParameterType.GetElementType() : 38 | parameterInfo.ParameterType.GetType(); 39 | 40 | triggerAttribute = regSvcTrigger; 41 | triggerFunction = regSvcFunctionName; 42 | } 43 | 44 | public bool FromAttribute => true; 45 | 46 | public Task BindAsync(object value, ValueBindingContext context) 47 | => Task.FromResult((IValueProvider)new InjectValueProvider(value)); 48 | 49 | public async Task BindAsync(BindingContext context) 50 | { 51 | var provider = triggerAttribute.GetServiceProvider(triggerFunction); 52 | var scope = triggerAttribute.Scopes.GetOrAdd(context.FunctionInstanceId, (_) => provider.CreateScope()); 53 | 54 | 55 | var services = hasDefaultValue ? 56 | ( 57 | isArray ? 58 | BindAsync( 59 | scope.ServiceProvider.GetServices(elementType), 60 | context.ValueContext 61 | ) 62 | : 63 | BindAsync( 64 | scope.ServiceProvider.GetService(elementType), 65 | context.ValueContext 66 | ) 67 | ) : ( 68 | isArray ? 69 | BindAsync( 70 | scope.ServiceProvider.GetServices(elementType), 71 | context.ValueContext 72 | ) 73 | : 74 | BindAsync( 75 | scope.ServiceProvider.GetRequiredService(elementType), 76 | context.ValueContext 77 | ) 78 | ); 79 | return await services; 80 | } 81 | 82 | public ParameterDescriptor ToParameterDescriptor() => new ParameterDescriptor(); 83 | 84 | private class InjectValueProvider : IValueProvider 85 | { 86 | private readonly object value; 87 | 88 | public InjectValueProvider(object value) => this.value = value; 89 | 90 | public Type Type => value.GetType(); 91 | 92 | public Task GetValueAsync() => Task.FromResult(value); 93 | 94 | public string ToInvokeString() => value.ToString(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /FuncInjector/Injection/InjectBindingProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Host.Bindings; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | 5 | namespace FuncInjector 6 | { 7 | /// 8 | /// The factory for InjectBinding. 9 | /// 10 | public class InjectBindingProvider : IBindingProvider 11 | { 12 | private readonly RegisterServicesTrigger config; 13 | 14 | public InjectBindingProvider(RegisterServicesTrigger configuration) => config = configuration; 15 | 16 | public Task TryCreateAsync(BindingProviderContext context) 17 | { 18 | var parameter = context.Parameter; 19 | var attribute = parameter.GetCustomAttribute(false); 20 | IBinding binding = new InjectBinding(context.Parameter, config, attribute.RegisterServicesFunctionName); 21 | return Task.FromResult(binding); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /FuncInjector/RegisterServicesTrigger/RegisterServicesTrigger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading; 4 | using Microsoft.Azure.WebJobs; 5 | using Microsoft.Azure.WebJobs.Host; 6 | using Microsoft.Azure.WebJobs.Host.Config; 7 | using Microsoft.Azure.WebJobs.Host.Executors; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace FuncInjector 11 | { 12 | /// 13 | /// Initialization and Lazy storage of all RegisterServices triggers. 14 | /// 15 | public class RegisterServicesTrigger : IExtensionConfigProvider 16 | { 17 | public readonly ConcurrentDictionary Scopes = new ConcurrentDictionary(); 18 | 19 | private readonly ConcurrentDictionary> configFnExecs = new ConcurrentDictionary>(); 20 | 21 | public void Initialize(ExtensionConfigContext context) 22 | { 23 | context 24 | .AddBindingRule() 25 | .Bind(new InjectBindingProvider(this)); 26 | var registry = context.Config.GetService(); 27 | var filter = new ReleaseScopedServicesFilter(this); 28 | registry.RegisterExtension(typeof(IFunctionInvocationFilter), filter); 29 | registry.RegisterExtension(typeof(IFunctionExceptionFilter), filter); 30 | context.Config.RegisterBindingExtensions(new RegisterServicesTriggerBindingProvider(this)); 31 | } 32 | 33 | public void AddConfigExecutor(string functionName, ITriggeredFunctionExecutor executor) 34 | { 35 | var lazy = new Lazy(() => 36 | { 37 | var services = new ServiceCollection(); 38 | executor.TryExecuteAsync(new TriggeredFunctionData() {TriggerValue = services}, CancellationToken.None).GetAwaiter().GetResult(); 39 | return services.BuildServiceProvider(); 40 | }); 41 | configFnExecs.TryAdd(functionName, lazy); 42 | } 43 | 44 | public IServiceProvider GetServiceProvider(string functionName) 45 | { 46 | if (configFnExecs.TryGetValue(functionName, out var result)) 47 | { 48 | return result.Value; 49 | } 50 | throw new Exception($"RegisterServicesTrigger function {functionName} not found."); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /FuncInjector/RegisterServicesTrigger/RegisterServicesTriggerAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Description; 2 | using System; 3 | 4 | namespace FuncInjector 5 | { 6 | /// 7 | /// Declaration of the RegisterServicesTrigger attribute. 8 | /// 9 | [Binding] 10 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] 11 | public class RegisterServicesTriggerAttribute : Attribute { } 12 | } -------------------------------------------------------------------------------- /FuncInjector/RegisterServicesTrigger/RegisterServicesTriggerBinding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Reflection; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.Azure.WebJobs.Description; 8 | using Microsoft.Azure.WebJobs.Extensions.Bindings; 9 | using Microsoft.Azure.WebJobs.Host.Bindings; 10 | using Microsoft.Azure.WebJobs.Host.Listeners; 11 | using Microsoft.Azure.WebJobs.Host.Protocols; 12 | using Microsoft.Azure.WebJobs.Host.Triggers; 13 | using Microsoft.Extensions.DependencyInjection; 14 | 15 | namespace FuncInjector 16 | { 17 | /// 18 | /// Binds the trigger to an IServicesCollection type and creates a basic Listener 19 | /// driven by a value-request from an [Inject] attribute. 20 | /// 21 | public class RegisterServicesTriggerBinding : ITriggerBinding 22 | { 23 | private readonly Dictionary bindingContract 24 | = new Dictionary(StringComparer.CurrentCultureIgnoreCase) 25 | { 26 | {"data", typeof(IServiceCollection)} 27 | }; 28 | 29 | private readonly RegisterServicesTrigger configuration; 30 | private readonly ParameterInfo parameter; 31 | public RegisterServicesTriggerBinding(ParameterInfo parameter, RegisterServicesTrigger configuration) 32 | { 33 | this.parameter = parameter; 34 | this.configuration = configuration; 35 | } 36 | 37 | public Task BindAsync(object value, ValueBindingContext context) 38 | { 39 | if(value is IServiceCollection) 40 | { 41 | IValueBinder binder = new RegisterServicesTriggerValueBinder(value as IServiceCollection, parameter.ParameterType); 42 | return Task.FromResult((ITriggerData)new TriggerData(binder, 43 | new Dictionary(StringComparer.CurrentCultureIgnoreCase) 44 | { 45 | {"data", value} 46 | })); 47 | } 48 | throw new NotSupportedException(); 49 | } 50 | 51 | public Task CreateListenerAsync(ListenerFactoryContext context) 52 | { 53 | configuration.AddConfigExecutor(context.Descriptor.ShortName, context.Executor); 54 | return Task.FromResult((IListener)new RegisterServicesTriggerListener()); 55 | } 56 | 57 | public ParameterDescriptor ToParameterDescriptor() => new RegisterServicesTriggerParameterDescriptor(); 58 | 59 | public Type TriggerValueType => typeof(IServiceCollection); 60 | 61 | public IReadOnlyDictionary BindingDataContract => bindingContract; 62 | 63 | private class RegisterServicesTriggerValueBinder : ValueBinder, IDisposable 64 | { 65 | private readonly IServiceCollection services; 66 | public RegisterServicesTriggerValueBinder(IServiceCollection serviceCollection, Type type, BindStepOrder bindStepOrder = BindStepOrder.Default) : base(type, bindStepOrder) 67 | => services = serviceCollection; 68 | 69 | public void Dispose() { } 70 | 71 | public override Task GetValueAsync() => Task.FromResult((object)services); 72 | 73 | public override string ToInvokeString() => string.Empty; 74 | } 75 | 76 | private class RegisterServicesTriggerParameterDescriptor : TriggerParameterDescriptor 77 | { 78 | public override string GetTriggerReason(IDictionary arguments) 79 | { 80 | // TODO: Customize your Dashboard display string 81 | return string.Format("RegisterServicesTrigger trigger fired at {0}", DateTime.Now.ToString("o")); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /FuncInjector/RegisterServicesTrigger/RegisterServicesTriggerBindingProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | using Microsoft.Azure.WebJobs.Host.Triggers; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace FuncInjector 9 | { 10 | /// 11 | /// Factory for RegisterServicesTriggerBinding. 12 | /// 13 | public class RegisterServicesTriggerBindingProvider : ITriggerBindingProvider 14 | { 15 | private readonly RegisterServicesTrigger configuration; 16 | 17 | public RegisterServicesTriggerBindingProvider(RegisterServicesTrigger configuration) => this.configuration = configuration; 18 | 19 | public Task TryCreateAsync(TriggerBindingProviderContext context) 20 | { 21 | if(context == null) throw new ArgumentNullException("context"); 22 | var parameter = context.Parameter; 23 | var attribute = parameter.GetCustomAttribute(false); 24 | if(attribute == null) return Task.FromResult(null); 25 | if(!IsSupportBindingType(parameter.ParameterType)) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Can't bind RegisterServicesTriggerAttribute to type '{0}'.", parameter.ParameterType)); 26 | return Task.FromResult(new RegisterServicesTriggerBinding(context.Parameter, configuration)); 27 | } 28 | 29 | public bool IsSupportBindingType(Type t) => t == typeof(IServiceCollection); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FuncInjector/RegisterServicesTrigger/RegisterServicesTriggerListener.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Host.Listeners; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace FuncInjector 6 | { 7 | /// 8 | /// RegisterServices trigger only needs to provide basic start/stop acknowledgements. 9 | /// 10 | public class RegisterServicesTriggerListener : IListener 11 | { 12 | public void Dispose() { } 13 | 14 | public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; 15 | 16 | public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; 17 | 18 | public void Cancel() { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FuncInjector/RegisterServicesTrigger/ReleaseScopedServicesFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs.Host; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace FuncInjector 7 | { 8 | /// 9 | /// Tear-down of any scoped-lifetime services. 10 | /// 11 | public class ReleaseScopedServicesFilter : IFunctionInvocationFilter, IFunctionExceptionFilter 12 | { 13 | private readonly RegisterServicesTrigger trigger; 14 | 15 | public ReleaseScopedServicesFilter(RegisterServicesTrigger regSvcTrigger) => trigger = regSvcTrigger; 16 | 17 | public Task OnExceptionAsync(FunctionExceptionContext context, CancellationToken cancellationToken) 18 | { 19 | RemoveScope(context.FunctionInstanceId); 20 | return Task.CompletedTask; 21 | } 22 | 23 | public Task OnExecutedAsync(FunctionExecutedContext context, CancellationToken cancellationToken) 24 | { 25 | RemoveScope(context.FunctionInstanceId); 26 | return Task.CompletedTask; 27 | } 28 | 29 | public Task OnExecutingAsync(FunctionExecutingContext context, CancellationToken cancellationToken) => Task.CompletedTask; 30 | 31 | private void RemoveScope(Guid id) 32 | { 33 | if (trigger.Scopes.TryRemove(id, out var scope)) scope.Dispose(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /FunctionProject/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /FunctionProject/FunctionProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net461 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | PreserveNewest 22 | Never 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /FunctionProject/GreeterFunction.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.WebJobs; 2 | using Microsoft.Azure.WebJobs.Extensions.Http; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Azure.WebJobs.Host; 7 | using System; 8 | 9 | using FuncInjector; 10 | using Library; 11 | 12 | namespace FunctionProject 13 | { 14 | public static class GreeterFunction 15 | { 16 | [FunctionName("GreeterSingleton1")] 17 | public static async Task Run1( 18 | [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage req, 19 | [Inject("RegisterSingletons")] IGreeter greeter) 20 | { 21 | return req.CreateResponse(greeter.Greet()); 22 | } 23 | 24 | [FunctionName("GreeterSingleton2")] 25 | public static async Task Run2( 26 | [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage req, 27 | [Inject("RegisterSingletons")] IGreeter greeter) 28 | { 29 | return req.CreateResponse(greeter.Greet()); 30 | } 31 | 32 | [FunctionName("GreeterScoped")] 33 | public static async Task Run3( 34 | [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage req, 35 | [Inject("RegisterScoped")] IGreeter greeter, 36 | [Inject("RegisterScoped")] IGreeterConsumer scoped) 37 | { 38 | return req.CreateResponse($"greetersvc {greeter.Greet()} --- scopedsvc {scoped.Greeting()}"); 39 | } 40 | 41 | [FunctionName("GreeterNonScoped")] 42 | public static async Task Run4( 43 | [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage req, 44 | [Inject("RegisterNonScoped")] IGreeter greeter, 45 | [Inject("RegisterNonScoped")] IGreeterConsumer scoped) 46 | { 47 | return req.CreateResponse($"greetersvc {greeter.Greet()} --- scopedsvc {scoped.Greeting()}"); 48 | } 49 | 50 | [FunctionName("GreeterTransient")] 51 | public static async Task Run5( 52 | [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage req, 53 | [Inject("RegisterTransient")] IGreeter greeter) 54 | { 55 | return req.CreateResponse(greeter.Greet()); 56 | } 57 | 58 | [FunctionName("GreeterDefaultReg")] 59 | public static async Task Run6( 60 | [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestMessage req, 61 | [Inject] IGreeter greeter) 62 | { 63 | return req.CreateResponse(greeter.Greet()); 64 | } 65 | 66 | [FunctionName("RegisterSingletons")] 67 | public static void Config1([RegisterServicesTrigger] IServiceCollection services) 68 | { 69 | services.AddGreeterSingleton(); 70 | } 71 | 72 | [FunctionName("RegisterScoped")] 73 | public static void Config2([RegisterServicesTrigger] IServiceCollection services) 74 | { 75 | services.AddGreeterScoped(); 76 | services.AddGreeterConsumer(); 77 | } 78 | 79 | [FunctionName("RegisterNonScoped")] 80 | public static void Config3([RegisterServicesTrigger] IServiceCollection services) 81 | { 82 | services.AddGreeterTransient(); 83 | services.AddGreeterConsumer(); 84 | } 85 | 86 | [FunctionName("RegisterTransient")] 87 | public static void Config4([RegisterServicesTrigger] IServiceCollection services) 88 | { 89 | services.AddGreeterTransient(); 90 | } 91 | 92 | [FunctionName("RegisterServices")] // default registration trigger name for [Inject] 93 | public static void Config5([RegisterServicesTrigger] IServiceCollection services) 94 | { 95 | services.AddGreeterTransient(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /FunctionProject/Properties/PublishProfiles/mv10blog - Web Deploy.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | MSDeploy 9 | AzureWebSite 10 | Release 11 | Any CPU 12 | http://mv10blog.azurewebsites.net 13 | False 14 | False 15 | mv10blog.scm.azurewebsites.net:443 16 | /subscriptions/903b79d1-bf21-43e4-977f-d09346ba54f8/resourceGroups/mv10-blog/providers/Microsoft.Web/sites/mv10blog 17 | mv10blog 18 | False 19 | WMSVC 20 | True 21 | $mv10blog 22 | <_SavePWD>True 23 | False 24 | net461 25 | 26 | -------------------------------------------------------------------------------- /FunctionProject/host.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Boris Wilhelms 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Library/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /Library/GreeterService/AGreeter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Library 4 | { 5 | /// 6 | /// A basic implementation of IGreeter. The constructed property's Millisecond value 7 | /// can be used to compare instances. 8 | /// 9 | public class AGreeter : IGreeter 10 | { 11 | public readonly DateTimeOffset constructed; 12 | public AGreeter() 13 | => constructed = DateTimeOffset.UtcNow; 14 | 15 | public string Greet() 16 | => $"Hello World, no counter, ms:{constructed.Millisecond}"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Library/GreeterService/CountUpGreeter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Library 6 | { 7 | /// 8 | /// An implementation of IGreeter which depends on ICounter. By incrementing 9 | /// the counter we can demonstrate singleton lifetime services. 10 | /// 11 | public class CountUpGreeter : AGreeter, IGreeter 12 | { 13 | private readonly ICounter counter; 14 | public CountUpGreeter(ICounter counter) : base() 15 | => this.counter = counter; 16 | 17 | public new string Greet() 18 | => $"Hello World, counter {counter.count++}, ms:{constructed.Millisecond}"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Library/GreeterService/Counter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Library 6 | { 7 | /// 8 | /// A simple container for an integer so we can demonstrate library-level injection. 9 | /// 10 | public class Counter : ICounter 11 | { 12 | public int count { get; set; } = 0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Library/GreeterService/GreeterInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Library 4 | { 5 | /// 6 | /// Service registration helpers for IGreeter implementations and related dependencies. 7 | /// 8 | public static class GreeterInjectionExtensions 9 | { 10 | public static IServiceCollection AddGreeterSingleton(this IServiceCollection services) 11 | { 12 | services.AddSingleton(); 13 | services.AddSingleton(); 14 | return services; 15 | } 16 | 17 | public static IServiceCollection AddGreeterScoped(this IServiceCollection services) 18 | { 19 | services.AddScoped(); 20 | services.AddScoped(); 21 | return services; 22 | } 23 | 24 | public static IServiceCollection AddGreeterTransient(this IServiceCollection services) 25 | { 26 | services.AddTransient(); 27 | services.AddTransient(); 28 | return services; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Library/GreeterService/ICounter.cs: -------------------------------------------------------------------------------- 1 | namespace Library 2 | { 3 | public interface ICounter 4 | { 5 | int count { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Library/GreeterService/IGreeter.cs: -------------------------------------------------------------------------------- 1 | namespace Library 2 | { 3 | public interface IGreeter 4 | { 5 | string Greet(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Library/Library.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Library/ScopedService/GreeterConsumer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Library 6 | { 7 | /// 8 | /// A class which is "external" to the IGreeter services but depends upon them. Used 9 | /// to demonstrate scoped lifetime services. 10 | /// 11 | public class GreeterConsumer : IGreeterConsumer 12 | { 13 | private readonly IGreeter greeter; 14 | public GreeterConsumer(IGreeter greeter) 15 | => this.greeter = greeter; 16 | 17 | public string Greeting() 18 | => greeter.Greet(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Library/ScopedService/GreeterConsumerInjectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Library 7 | { 8 | /// 9 | /// A helper to properly register the IGreeterConsumer service. 10 | /// 11 | public static class GreeterConsumerInjectionExtensions 12 | { 13 | public static IServiceCollection AddGreeterConsumer(this IServiceCollection services) 14 | { 15 | services.AddTransient(); 16 | return services; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Library/ScopedService/IGreeterConsumer.cs: -------------------------------------------------------------------------------- 1 | namespace Library 2 | { 3 | public interface IGreeterConsumer 4 | { 5 | string Greeting(); 6 | } 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Repository for code from my 2018-Feb-01 blog post: 2 | 3 | ### [Reusable Dependency Injection for Azure Function Apps](https://mcguirev10.com/2018/02/01/reusable-dependency-injection-azure-function.html) 4 | 5 | --- 6 | 7 | Based upon work in these repos: 8 | 9 | [BorisWilhelms](https://github.com/BorisWilhelms/azure-function-dependency-injection) 10 | 11 | [yuka1984](https://github.com/yuka1984/azure-function-dependency-injection) 12 | 13 | --- 14 | 15 | 2018-04 About Functions V2: 16 | 17 | I'd hoped to update this library to support Functions V2, however all of the `netstandard20`-compatible releases of the SDK and the webjob assemblies throw various binding errors (most recently while running locally, and in all cases when deployed to Azure). I spent a couple days researching the problem and trying various things, and I've concluded that Functions are moving in a direction that may make custom trigger bindings impossible to build. 18 | 19 | Unfortunately [this](https://github.com/Azure/azure-webjobs-sdk/wiki/Creating-custom-input-and-output-bindings) Functions wiki even states "Custom triggers are not available for Azure Functions," and various Microsoft folks have responded to Stack Overflow questions that custom triggers aren't in-scope. 20 | 21 | Since the service registration custom trigger is part of what made this DI library so convenient to use, and given that Microsoft is working on adding DI support to Functions (it sounds like we might have to wait for the runtime 3.0 release -- still Functions V2, terrible choice of names there), I'm not burning any more time (or Azure billing) on V2 support. 22 | 23 | The V2 changes likely would have been pretty simple, apart from the new extensions installation/registration gynmastics (which supposedly will go away). I'll leave the changes in a separate branch in case someone else has more time to kill than I do! 24 | 25 | https://github.com/MV10/Azure.Functions.Dependency.Injection/tree/FunctionsV2 26 | 27 | See also: 28 | 29 | ### [Service Locator for Azure Functions V2](https://mcguirev10.com/2018/04/03/service-locator-azure-functions-v2.html) 30 | --------------------------------------------------------------------------------