void IExtensionConfigProvider.Initialize(ExtensionConfigContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            // Deferred list
            foreach (var action in _deferredWork)
            {
                action(context.Config);
            }
            _deferredWork.Clear();

            // get the services we need to construct our binding providers
            INameResolver      nameResolver = context.Config.NameResolver;
            IExtensionRegistry extensions   = context.Config.GetService <IExtensionRegistry>();

            IConverterManager cm = context.Config.GetService <IConverterManager>();

            cm.AddConverter <string, EventData>(ConvertString2EventData);
            cm.AddConverter <EventData, string>(ConvertEventData2String);
            cm.AddConverter <byte[], EventData>(ConvertBytes2EventData); // direct, handles non-string representations

            // register our trigger binding provider
            var triggerBindingProvider = new EventHubTriggerAttributeBindingProvider(nameResolver, cm, this);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);

            // register our binding provider
            var bindingProvider = new EventHubAttributeBindingProvider(nameResolver, cm, this);

            extensions.RegisterExtension <IBindingProvider>(bindingProvider);
        }
Beispiel #2
0
        /// <summary>
        /// Initialize the IServiceProvider and add binding rule for the function framework. This is called by the framework.
        /// </summary>
        /// <param name="context">Context for function execution</param>
        public virtual void Initialize(ExtensionConfigContext context)
        {
            var path = System.Reflection.Assembly.GetExecutingAssembly().Location.Split(new[] { @"\bin" }, StringSplitOptions.None)[0];

            var builder = new ConfigurationBuilder()
                          .SetBasePath(path)
                          .AddJsonFile("settings.json");

            Configuration = builder.Build();

            IServiceCollection services = new ServiceCollection();

            services.AddDbContext <DatabaseContext>(options => options.UseSqlServer(Configuration["Storage:ConnectionString"]));
            RegisterServices(services);
            IServiceProvider serviceProvider = services.BuildServiceProvider(true);

            context
            .AddBindingRule <TAttribute>()
            .Bind(new InjectBindingProvider(serviceProvider));

            IExtensionRegistry registry = context.Config.GetService <IExtensionRegistry>();
            ScopeCleanupFilter filter   = new ScopeCleanupFilter();

            registry.RegisterExtension(typeof(IFunctionInvocationFilter), filter);
            registry.RegisterExtension(typeof(IFunctionExceptionFilter), filter);
        }
Beispiel #3
0
        /// <inheritdoc />
        public void Initialize(ExtensionConfigContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            // Set the default exception handler for background exceptions
            // coming from MessageReceivers.
            Config.ExceptionHandler = (e) =>
            {
                LogExceptionReceivedEvent(e, context.Config.LoggerFactory);
            };

            // get the services we need to construct our binding providers
            INameResolver      nameResolver = context.Config.GetService <INameResolver>();
            IExtensionRegistry extensions   = context.Config.GetService <IExtensionRegistry>();

            // register our trigger binding provider
            ServiceBusTriggerAttributeBindingProvider triggerBindingProvider = new ServiceBusTriggerAttributeBindingProvider(nameResolver, _serviceBusConfig);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);

            // register our binding provider
            ServiceBusAttributeBindingProvider bindingProvider = new ServiceBusAttributeBindingProvider(nameResolver, _serviceBusConfig);

            extensions.RegisterExtension <IBindingProvider>(bindingProvider);
        }
Beispiel #4
0
        public void GetService_IExtensionRegistry_ReturnsDefaultRegistry()
        {
            JobHostConfiguration configuration = new JobHostConfiguration();

            IExtensionRegistry extensionRegistry = configuration.GetService <IExtensionRegistry>();

            extensionRegistry.RegisterExtension <IComparable>("test1");
            extensionRegistry.RegisterExtension <IComparable>("test2");
            extensionRegistry.RegisterExtension <IComparable>("test3");

            Assert.NotNull(extensionRegistry);
            IComparable[] results = extensionRegistry.GetExtensions <IComparable>().ToArray();
            Assert.Equal(3, results.Length);
        }
Beispiel #5
0
        void IExtensionConfigProvider.Initialize(ExtensionConfigContext context)
        {
            INameResolver     nameResolver = context.Config.GetService <INameResolver>();
            IConverterManager cm           = context.Config.GetService <IConverterManager>();

            cm.AddConverter <string, FakeQueueData>(x => new FakeQueueData {
                Message = x
            });

            if (this.SetConverters != null)
            {
                this.SetConverters(cm);
            }

            cm.AddConverter <FakeQueueData, string>(msg => msg.Message);
            cm.AddConverter <OtherFakeQueueData, FakeQueueData>(OtherFakeQueueData.ToEvent);

            IExtensionRegistry extensions = context.Config.GetService <IExtensionRegistry>();

            var bf = new BindingFactory(nameResolver, cm);

            // Binds [FakeQueue] --> IAsyncCollector<FakeQueueData>
            var ruleOutput = bf.BindToAsyncCollector <FakeQueueAttribute, FakeQueueData>(BuildFromAttr);

            // Binds [FakeQueue] --> FakeQueueClient
            var ruleClient = bf.BindToExactType <FakeQueueAttribute, FakeQueueClient>((attr) => this);

            extensions.RegisterBindingRules <FakeQueueAttribute>(ruleOutput, ruleClient);

            var triggerBindingProvider = new FakeQueueTriggerBindingProvider(this, cm);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);
        }
Beispiel #6
0
        // Helper to send items to the listener, and return what they collected
        private object[] Run <TFunction>(params FakeQueueData[] items) where TFunction : FunctionsBase, new()
        {
            var activator = new FakeActivator();
            var func1     = new TFunction();

            activator.Add(func1);

            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator  = new FakeTypeLocator(typeof(TFunction)),
                JobActivator = activator
            };

            FakeQueueClient    client     = new FakeQueueClient();
            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(client);

            JobHost host = new JobHost(config);

            foreach (var item in items)
            {
                client.AddAsync(item).Wait();
            }

            host.Start();
            TestHelpers.WaitOne(func1._stopEvent);
            host.Stop();

            return(func1._collected.ToArray());
        }
Beispiel #7
0
        void IExtensionConfigProvider.Initialize(ExtensionConfigContext context)
        {
            var rule = context.AddBindingRule <FakeQueueAttribute>();

            context.AddConverter <string, FakeQueueData>(x => new FakeQueueData {
                Message = x
            });
            context.AddConverter <FakeQueueData, string>(msg => msg.Message);
            context.AddConverter <OtherFakeQueueData, FakeQueueData>(OtherFakeQueueData.ToEvent);

            rule.AddOpenConverter <OpenType.Poco, FakeQueueData>(ConvertPocoToFakeQueueMessage);

            INameResolver     nameResolver = context.Config.GetService <INameResolver>();
            IConverterManager cm           = context.Config.GetService <IConverterManager>();

            if (this.SetConverters != null)
            {
                this.SetConverters(context);
            }

            IExtensionRegistry extensions = context.Config.GetService <IExtensionRegistry>();

            // Binds [FakeQueue] --> IAsyncCollector<FakeQueueData>
            rule.BindToCollector <FakeQueueData>(BuildFromAttr);

            // Binds [FakeQueue] --> FakeQueueClient
            rule.BindToInput <FakeQueueClient>(this);

            var triggerBindingProvider = new FakeQueueTriggerBindingProvider(this, cm);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);
        }
        private JobHost CreateTestJobHost(int hostId)
        {
            TestJobActivator activator = new TestJobActivator(hostId);

            JobHostConfiguration config = new JobHostConfiguration
            {
                HostId       = TestHostId,
                NameResolver = _resolver,
                TypeLocator  = new FakeTypeLocator(typeof(TestJobs)),
                JobActivator = activator
            };

            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());
            config.Queues.MaxPollingInterval                = TimeSpan.FromSeconds(2);
            config.Singleton.LockAcquisitionTimeout         = TimeSpan.FromSeconds(10);
            config.Singleton.LockAcquisitionPollingInterval = TimeSpan.FromMilliseconds(500);

            IExtensionRegistry registry = config.GetService <IExtensionRegistry>();

            registry.RegisterExtension <ITriggerBindingProvider>(new TestTriggerAttributeBindingProvider());

            JobHost host = new JobHost(config);

            return(host);
        }
        void IExtensionConfigProvider.Initialize(ExtensionConfigContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            _defaultStorageString = context.Config.StorageConnectionString;

            // get the services we need to construct our binding providers
            INameResolver      nameResolver = context.Config.NameResolver;
            IExtensionRegistry extensions   = context.Config.GetService <IExtensionRegistry>();

            IConverterManager cm = context.Config.GetService <IConverterManager>();

            cm.AddConverter <string, EventData>(ConvertString2EventData);
            cm.AddConverter <EventData, string>(ConvertEventData2String);
            cm.AddConverter <byte[], EventData>(ConvertBytes2EventData); // direct, handles non-string representations
            cm.AddConverter <EventData, byte[]>(ConvertEventData2Bytes); // direct, handles non-string representations

            var bf = new BindingFactory(nameResolver, cm);

            // register our trigger binding provider
            var triggerBindingProvider = new EventHubTriggerAttributeBindingProvider(nameResolver, cm, this);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);

            // register our binding provider
            var ruleOutput = bf.BindToAsyncCollector <EventHubAttribute, EventData>(BuildFromAttribute);

            extensions.RegisterBindingRules <EventHubAttribute>(ruleOutput);
        }
        /// <summary>
        /// Registers the specified instance.
        /// </summary>
        /// <typeparam name="TExtension">The service type to register the instance for.</typeparam>
        /// <param name="registry">The registry instance.</param>
        /// <param name="extension">The instance to register.</param>
        public static void RegisterExtension <TExtension>(this IExtensionRegistry registry, TExtension extension)
        {
            if (registry == null)
            {
                throw new ArgumentNullException("registry");
            }

            registry.RegisterExtension(typeof(TExtension), extension);
        }
        public void Test()
        {
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator = new FakeTypeLocator(typeof(Functions))
            };

            FakeQueueClient    client     = new FakeQueueClient();
            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(client);

            JobHost host = new JobHost(config);

            // Single items
            var p1 = InvokeJson <Payload>(host, client, "SendOnePoco");

            Assert.Equal(1, p1.Length);
            Assert.Equal(123, p1[0].val1);

            var p2 = Invoke(host, client, "SendOneNative");

            Assert.Equal(1, p2.Length);
            Assert.Equal("message", p2[0].Message);
            Assert.Equal("extra", p2[0].ExtraPropertery);

            var p3 = Invoke(host, client, "SendOneString");

            Assert.Equal(1, p3.Length);
            Assert.Equal("stringvalue", p3[0].Message);

            foreach (string methodName in new string[] { "SendDontQueue", "SendArrayNull", "SendArrayLen0" })
            {
                var p6 = Invoke(host, client, methodName);
                Assert.Equal(0, p6.Length);
            }

            // batching
            foreach (string methodName in new string[] {
                "SendSyncCollectorBytes", "SendArrayString", "SendSyncCollectorString", "SendAsyncCollectorString", "SendCollectorNative"
            })
            {
                var p4 = Invoke(host, client, methodName);
                Assert.Equal(2, p4.Length);
                Assert.Equal("first", p4[0].Message);
                Assert.Equal("second", p4[1].Message);
            }

            foreach (string methodName in new string[] { "SendCollectorPoco", "SendArrayPoco" })
            {
                var p5 = InvokeJson <Payload>(host, client, methodName);
                Assert.Equal(2, p5.Length);
                Assert.Equal(100, p5[0].val1);
                Assert.Equal(200, p5[1].val1);
            }
        }
Beispiel #12
0
        void IExtensionConfigProvider.Initialize(ExtensionConfigContext context)
        {
            IConverterManager cm = context.Config.GetOrCreateConverterManager();

            cm.AddConverter <string, FakeQueueData>(x => new FakeQueueData {
                Message = x
            });
            cm.AddConverter <FakeQueueData, string>(msg => msg.Message);

            IExtensionRegistry extensions = context.Config.GetService <IExtensionRegistry>();

            var bindingProvider = new FakeQueueBindingProvider(this, cm);

            extensions.RegisterExtension <IBindingProvider>(bindingProvider);

            var triggerBindingProvider = new FakeQueueTriggerBindingProvider(this, cm);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);
        }
Beispiel #13
0
        /// <summary>
        /// Setup a trigger binding for this attribute
        /// </summary>
        /// <param name="trigger"></param>
        public void BindToTrigger(ITriggerBindingProvider trigger)
        {
            if (_binders.Count > 0)
            {
                throw new InvalidOperationException($"The same attribute can't be bound to trigger and non-trigger bindings");
            }
            IExtensionRegistry extensions = _parent.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <ITriggerBindingProvider>(trigger);
        }
Beispiel #14
0
        // Helper to create a JobHostConfiguraiton from a set of services.
        // Default config, pure-in-memory
        // Default is pure-in-memory, good for unit testing.
        public static JobHostConfiguration NewConfig(
            Type functions,
            params object[] services
            )
        {
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator = new FakeTypeLocator(functions),

                // Pure in-memory, no storage.
                HostId = Guid.NewGuid().ToString("n"),
                DashboardConnectionString = null,
                StorageConnectionString   = null
            };

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            foreach (var obj in services)
            {
                IStorageAccount account = obj as IStorageAccount;
                if (account != null)
                {
                    IStorageAccountProvider storageAccountProvider = new FakeStorageAccountProvider
                    {
                        StorageAccount = account
                    };
                    config.ContextFactory = new JobHostContextFactory(storageAccountProvider, new DefaultConsoleProvider(), config);
                    continue;
                }

                INameResolver nameResolver = obj as INameResolver;
                if (nameResolver != null)
                {
                    config.NameResolver = nameResolver;
                    continue;
                }
                IJobActivator jobActivator = obj as IJobActivator;
                if (jobActivator != null)
                {
                    config.JobActivator = jobActivator;
                    continue;
                }

                IExtensionConfigProvider extension = obj as IExtensionConfigProvider;
                if (extension != null)
                {
                    extensions.RegisterExtension <IExtensionConfigProvider>(extension);
                    continue;
                }

                throw new InvalidOperationException("Test bug: Unrecognized type: " + obj.GetType().FullName);
            }

            return(config);
        }
Beispiel #15
0
        /// <summary>
        /// Registers the specified extension with the <see cref="JobHostConfiguration"/>
        /// </summary>
        /// <typeparam name="TExtension">The type of extension being registered.</typeparam>
        /// <param name="config">The <see cref="JobHostConfiguration"/> to register the extension with.</param>
        /// <param name="extension">The extension to register.</param>
        public static void RegisterExtension <TExtension>(this JobHostConfiguration config, TExtension extension)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <TExtension>(extension);
        }
Beispiel #16
0
        /// <inheritdoc />
        public void Initialize(ExtensionConfigContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            // get the services we need to construct our binding providers
            INameResolver      nameResolver = context.Config.GetService <INameResolver>();
            IExtensionRegistry extensions   = context.Config.GetService <IExtensionRegistry>();

            // register our trigger binding provider
            ServiceBusTriggerAttributeBindingProvider triggerBindingProvider = new ServiceBusTriggerAttributeBindingProvider(nameResolver, _serviceBusConfig);

            extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);

            // register our binding provider
            ServiceBusAttributeBindingProvider bindingProvider = new ServiceBusAttributeBindingProvider(nameResolver, _serviceBusConfig);

            extensions.RegisterExtension <IBindingProvider>(bindingProvider);
        }
        public static void RegisterBindingRules <TAttribute>(this IExtensionRegistry registry, params IBindingProvider[] bindingProviders)
            where TAttribute : Attribute
        {
            if (registry == null)
            {
                throw new ArgumentNullException("registry");
            }

            var all = new GenericCompositeBindingProvider <TAttribute>(bindingProviders);

            registry.RegisterExtension <IBindingProvider>(all);
        }
Beispiel #18
0
        /// <summary>
        /// Add ApiHub configuration to <see cref="JobHostConfiguration"/>
        /// </summary>
        /// <param name="config">current <see cref="JobHostConfiguration"/></param>
        /// <param name="apiHubConfiguration">Instance of <see cref="ApiHubConfiguration"/></param>
        public static void UseApiHub(this JobHostConfiguration config, ApiHubConfiguration apiHubConfiguration = null)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (apiHubConfiguration == null)
            {
                apiHubConfiguration = new ApiHubConfiguration();
            }
            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(apiHubConfiguration);
        }
Beispiel #19
0
        public static void UseEventHub(this JobHostConfiguration config, EventHubConfiguration eventHubConfig)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (eventHubConfig == null)
            {
                throw new ArgumentNullException("eventHubConfig");
            }

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(eventHubConfig);
        }
        public static void RegisterBindingRules <TAttribute>(
            this IExtensionRegistry registry,
            Action <TAttribute, Type> validator,
            IConfiguration configuration,
            INameResolver nameResolver,
            params IBindingProvider[] bindingProviders)
            where TAttribute : Attribute
        {
            if (registry == null)
            {
                throw new ArgumentNullException("registry");
            }

            var all = new GenericCompositeBindingProvider <TAttribute>(validator, configuration, nameResolver, bindingProviders);

            registry.RegisterExtension <IBindingProvider>(all);
        }
        /// <summary>
        /// Enables use of ServiceBus job extensions
        /// </summary>
        /// <param name="config">The <see cref="JobHostConfiguration"/> to configure.</param>
        /// <param name="serviceBusConfig">The <see cref="ServiceBusConfiguration"></see> to use./></param>
        public static void UseServiceBus(this JobHostConfiguration config, ServiceBusConfiguration serviceBusConfig)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (serviceBusConfig == null)
            {
                throw new ArgumentNullException("serviceBusConfig");
            }

            ServiceBusExtensionConfig extensionConfig = new ServiceBusExtensionConfig(serviceBusConfig);

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(extensionConfig);
        }
        public static void UseDurableTask(
            this JobHostConfiguration hostConfig,
            DurableTaskExtension listenerConfig)
        {
            if (hostConfig == null)
            {
                throw new ArgumentNullException(nameof(hostConfig));
            }

            if (listenerConfig == null)
            {
                throw new ArgumentNullException(nameof(listenerConfig));
            }

            IExtensionRegistry extensions = hostConfig.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(listenerConfig);
        }
Beispiel #23
0
        // Called by infrastructure after the extension is invoked.
        // This applies all changes accumulated on the fluent object.
        internal void ApplyRules()
        {
            if (_hook != null)
            {
                throw new InvalidOperationException("SetPostResolveHook() should be called before the Bind() it applies to.");
            }
            if (_filters.Count > 0)
            {
                throw new InvalidOperationException($"Filters ({_filterDescription}) should be called before the Bind() they apply to.");
            }

            if (_binders.Count > 0)
            {
                IExtensionRegistry extensions = _parent.GetService <IExtensionRegistry>();
                var binding = CreateBinding();
                extensions.RegisterExtension <IBindingProvider>(binding);
                _binders.Clear();
            }
        }
Beispiel #24
0
            public void Initialize(ExtensionConfigContext context)
            {
                IExtensionRegistry extensions = context.Config.GetService <IExtensionRegistry>();
                var bf = context.Config.BindingFactory;

                // Add [Test] support
                var rule = bf.BindToExactType <TestAttribute, string>(attr => attr.Path);

                extensions.RegisterBindingRules <TestAttribute>(rule);

                // Add [FakeQueueTrigger] support.
                IConverterManager cm = context.Config.GetService <IConverterManager>();

                cm.AddConverter <string, FakeQueueData>(x => new FakeQueueData {
                    Message = x
                });
                cm.AddConverter <FakeQueueData, string>(msg => msg.Message);
                var triggerBindingProvider = new FakeQueueTriggerBindingProvider(new FakeQueueClient(), cm);

                extensions.RegisterExtension <ITriggerBindingProvider>(triggerBindingProvider);
            }
        public static void UseDurableTask(
            this JobHostConfiguration hostConfig,
            DurableTaskConfiguration listenerConfig)
        {
            if (hostConfig == null)
            {
                throw new ArgumentNullException(nameof(hostConfig));
            }

            if (listenerConfig == null)
            {
                throw new ArgumentNullException(nameof(listenerConfig));
            }

            IExtensionRegistry extensions = hostConfig.GetService <IExtensionRegistry>();

            extensions.RegisterExtension <IExtensionConfigProvider>(listenerConfig);

            // TODO: We try to disable dashboard logging because the dashboard logger's
            // network I/O causes DTFx orchestrations to hang. This is an open issue
            // that currently has no solution.
            hostConfig.DashboardConnectionString = null;
        }
        public static void AddServices(this JobHostConfiguration config, params object[] services)
        {
            // Set extensionRegistry first since other services may depend on it.
            foreach (var obj in services)
            {
                IExtensionRegistry extensionRegistry = obj as IExtensionRegistry;
                if (extensionRegistry != null)
                {
                    config.AddService <IExtensionRegistry>(extensionRegistry);
                    break;
                }
            }

            IExtensionRegistry extensions = config.GetService <IExtensionRegistry>();

            var types = new Type[] {
                typeof(IAsyncCollector <FunctionInstanceLogEntry>),
                typeof(IHostInstanceLoggerProvider),
                typeof(IFunctionInstanceLoggerProvider),
                typeof(IFunctionOutputLoggerProvider),
                typeof(IConsoleProvider),
                typeof(ITypeLocator),
                typeof(IWebJobsExceptionHandler),
                typeof(INameResolver),
                typeof(IJobActivator),
                typeof(IExtensionTypeLocator),
                typeof(SingletonManager),
                typeof(IHostIdProvider),
                typeof(IQueueConfiguration),
                typeof(IExtensionRegistry),
                typeof(IDistributedLockManager),
                typeof(ILoggerFactory),
                typeof(IFunctionIndexProvider) // set to unit test indexing.
            };

            foreach (var obj in services)
            {
                if (obj == null ||
                    obj is ILoggerProvider)
                {
                    continue;
                }

                IStorageAccountProvider storageAccountProvider = obj as IStorageAccountProvider;
                IStorageAccount         account = obj as IStorageAccount;
                if (account != null)
                {
                    storageAccountProvider = new FakeStorageAccountProvider
                    {
                        StorageAccount = account
                    };
                }
                if (storageAccountProvider != null)
                {
                    config.AddService <IStorageAccountProvider>(storageAccountProvider);
                    continue;
                }

                // A new extension
                IExtensionConfigProvider extension = obj as IExtensionConfigProvider;
                if (extension != null)
                {
                    extensions.RegisterExtension <IExtensionConfigProvider>(extension);
                    continue;
                }

                // A function filter
                if (obj is IFunctionInvocationFilter)
                {
                    extensions.RegisterExtension <IFunctionInvocationFilter>((IFunctionInvocationFilter)obj);
                    continue;
                }

                if (obj is IFunctionExceptionFilter)
                {
                    extensions.RegisterExtension <IFunctionExceptionFilter>((IFunctionExceptionFilter)obj);
                    continue;
                }

                // basic pattern.
                bool ok = false;
                foreach (var type in types)
                {
                    if (type.IsAssignableFrom(obj.GetType()))
                    {
                        config.AddService(type, obj);
                        ok = true;
                        break;
                    }
                }
                if (ok)
                {
                    continue;
                }

                throw new InvalidOperationException("Test bug: Unrecognized type: " + obj.GetType().FullName);
            }
        }