public void InstancePerTenant_RootAndPerTenantDependencies() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var builder = new ContainerBuilder(); builder.RegisterType<StubDependency3Impl>().As<IStubDependency3>().InstancePerTenant(); var mtc = new MultitenantContainer(strategy, builder.Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterType<StubDependency1Impl1>().As<IStubDependency1>().InstancePerTenant()); mtc.ConfigureTenant("tenant2", b => b.RegisterType<StubDependency1Impl1>().As<IStubDependency1>().InstancePerTenant()); // Two resolutions for a single tenant var dep1 = mtc.Resolve<IStubDependency3>(); var dep2 = mtc.Resolve<IStubDependency3>(); // One resolution for a different tenant strategy.TenantId = "tenant2"; var dep3 = mtc.Resolve<IStubDependency3>(); Assert.AreSame(dep1, dep2, "The two dependencies resolved for the first tenant should be the same."); Assert.AreNotSame(dep1, dep3, "The dependencies resolved across tenants should not be the same."); Assert.AreSame(dep1.Dependency, dep2.Dependency, "The two sub-dependencies resolved for the first tenant should be the same."); Assert.AreNotSame(dep1.Dependency, dep3.Dependency, "The sub-dependencies resolved across tenants should not be the same."); }
/// <summary> /// Configures the multitenant dependency container. /// </summary> private static IContainer ConfigureDependencies() { // Register default dependencies in the application container. var builder = new ContainerBuilder(); builder.RegisterType<Consumer>().As<IDependencyConsumer>().InstancePerDependency(); builder.RegisterType<BaseDependency>().As<IDependency>().SingleInstance(); var appContainer = builder.Build(); // Create the multitenant container. var mtc = new MultitenantContainer(_tenantIdentifier, appContainer); // Configure overrides for tenant 1. Tenant 1 registers their dependencies // as instance-per-dependency. mtc.ConfigureTenant('1', b => b.RegisterType<Tenant1Dependency>().As<IDependency>().InstancePerDependency()); // Configure overrides for tenant 2. Tenant 2 registers their dependencies // as singletons. mtc.ConfigureTenant('2', b => b.RegisterType<Tenant2Dependency>().As<IDependency>().SingleInstance()); // Configure overrides for the default tenant. That means the default // tenant will have some different dependencies than other unconfigured // tenants. mtc.ConfigureTenant(null, b => b.RegisterType<DefaultTenantDependency>().As<IDependency>().SingleInstance()); return mtc; }
protected void RegisterServices() { var tenantIdStrategy = new RouteDataItemTenantIdentificationStrategy("tenant"); var builder = new ContainerBuilder(); // Register your MVC controllers. builder.RegisterControllers(typeof(MvcApplication).Assembly); // OPTIONAL: Register model binders that require DI. builder.RegisterModelBinders(Assembly.GetExecutingAssembly()); builder.RegisterModelBinderProvider(); // OPTIONAL: Register web abstractions like HttpContextBase. builder.RegisterModule<AutofacWebTypesModule>(); // OPTIONAL: Enable property injection in view pages. builder.RegisterSource(new ViewRegistrationSource()); // OPTIONAL: Enable property injection into action filters. builder.RegisterFilterProvider(); // Create the multitenant container and the tenant overrides. var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); AddTenant(mtc, "a"); AddTenant(mtc, "b"); DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc)); }
public void Configuration(IAppBuilder app) { IContainer container = RegisterServices(); PrincipalTenantIdentificationStrategy tenantStrategy = new PrincipalTenantIdentificationStrategy(); MultitenantContainer mtc = new MultitenantContainer(tenantStrategy, container); HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttpRoute("DefaultHttpRoute", "api/{controller}"); app.UseAutofacContainer(mtc) .Use<RandomTextMiddleware>() .UseWebApiWithContainer(config); }
public void BeginLifetimeScope_ChildScopeCanBeTagged() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); using (var nestedScope = mtc.BeginLifetimeScope("tag")) { Assert.AreEqual("tag", nestedScope.Tag, "The child scope could not be tagged."); } }
public void RegisterDependencies(HttpConfiguration config) { var builder = new ContainerBuilder(); builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // creates a logger instance per tenant builder.RegisterType<LoggerService>().As<ILoggerService>().InstancePerTenant(); var mtc = new MultitenantContainer( new RequestParameterTenantIdentificationStrategy("tenant"), builder.Build()); config.DependencyResolver = new AutofacWebApiDependencyResolver(mtc); }
public void BeginLifetimeScope_ChildScopeCanBeConfigured() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterType<StubDependency1Impl1>().As<IStubDependency1>()); using (var nestedScope = mtc.BeginLifetimeScope(b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>())) { var nestedDependency = nestedScope.Resolve<IStubDependency1>(); Assert.IsInstanceOf<StubDependency1Impl2>(nestedDependency, "The child scope was not properly configured."); } }
public void BeginLifetimeScope_CreatesLifetimeScopeForCurrentTenant() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>().InstancePerLifetimeScope()); var tenantScope = mtc.GetCurrentTenantScope(); var tenantDependency = tenantScope.Resolve<IStubDependency1>(); using (var nestedScope = mtc.BeginLifetimeScope()) { var nestedDependency = nestedScope.Resolve<IStubDependency1>(); Assert.AreNotSame(tenantDependency, nestedDependency, "The dependency should be registered, but the scope should resolve a new instance."); } }
private static IContainer RegisterServices(ContainerBuilder builder, ITenantIdentificationStrategy idStrategy) { builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); builder.Register(c => idStrategy).As<ITenantIdentificationStrategy>(); builder.Register(c => { ITenantIdentificationStrategy s = c.Resolve<ITenantIdentificationStrategy>(); object tenantId; s.TryIdentifyTenant(out tenantId); return tenantId; }).Keyed<object>("tenantId"); // creates a logger instance per tenant builder.RegisterType<LoggerService>() .As<ILoggerService>().WithParameter((pi, c) => pi.Name == "tenant", (pi, c) => c.ResolveKeyed<object>("tenantId")).InstancePerTenant(); MultitenantContainer mtc = new MultitenantContainer( idStrategy, builder.Build()); return mtc; }
public void Resolve_TenantLevelSingleton() { var builder = new ContainerBuilder(); builder.RegisterType<StubDependency1Impl1>().As<IStubDependency1>().SingleInstance(); var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, builder.Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>().SingleInstance()); mtc.ConfigureTenant("tenant2", b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>().SingleInstance()); // Get the application-level dependency var appLevel = mtc.ApplicationContainer.Resolve<IStubDependency1>(); // Two resolutions for a single tenant var dep1 = mtc.Resolve<IStubDependency1>(); var dep2 = mtc.Resolve<IStubDependency1>(); // One resolution for a different tenant strategy.TenantId = "tenant2"; var dep3 = mtc.Resolve<IStubDependency1>(); Assert.IsInstanceOf<StubDependency1Impl2>(dep1, "Tenant 1's dependency should be the override value."); Assert.IsInstanceOf<StubDependency1Impl2>(dep3, "Tenant 2's dependency should be the override value."); Assert.IsInstanceOf<StubDependency1Impl1>(appLevel, "The application's dependency should be the base value."); Assert.AreSame(dep1, dep2, "The two dependencies resolved for the first tenant should be the same."); Assert.AreNotSame(dep1, dep3, "The dependencies resolved across tenants should not be the same."); Assert.AreNotSame(dep1, appLevel, "The dependencies resolved at the tenant level should not be the same as the ones at the application level."); }
public void Resolve_TenantFallbackToApplicationContainer() { var builder = new ContainerBuilder(); builder.RegisterType<StubDependency1Impl1>().As<IStubDependency1>(); var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, builder.Build()); Assert.IsInstanceOf<StubDependency1Impl1>(mtc.Resolve<IStubDependency1>(), "The wrong dependency type was resolved for the contextual tenant."); }
public void Resolve_ApplicationLevelSingleton() { var builder = new ContainerBuilder(); builder.RegisterType<StubDependency1Impl1>().As<IStubDependency1>().SingleInstance(); var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, builder.Build()); // Two resolutions for a single tenant var dep1 = mtc.Resolve<IStubDependency1>(); var dep2 = mtc.Resolve<IStubDependency1>(); // One resolution for a different tenant strategy.TenantId = "tenant2"; var dep3 = mtc.Resolve<IStubDependency1>(); Assert.AreSame(dep1, dep2, "The two dependencies resolved for the first tenant should be the same."); Assert.AreSame(dep1, dep3, "The dependencies resolved across tenants should be the same."); }
public void GetTenantScope_SubsequentRetrievalsGetTheSameLifetimeScope() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>()); var scope1 = mtc.GetTenantScope("tenant1"); var scope2 = mtc.GetTenantScope("tenant1"); Assert.AreSame(scope1, scope2, "The tenant scope should not change across subsequent retrievals."); }
public void GetTenantScope_NullIsDefaultTenant() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); var scope = mtc.GetTenantScope(null); Assert.IsNotNull(scope, "The default tenant scope should not be null."); Assert.AreNotSame(mtc.ApplicationContainer, scope, "The default tenant scope should be a real scope, not just the application container."); }
public void GetTenantScope_GetsTenantScopeForConfiguredTenant() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>()); var scope = mtc.GetTenantScope("tenant1"); Assert.IsNotNull(scope, "The tenant scope retrieved not be null."); Assert.AreNotSame(mtc.ApplicationContainer, scope, "The tenant scope should be a real scope, not just the application container."); }
/// <summary> /// Register dependencies to the multitenant container /// </summary> /// <param name="container">The Autofac multi tenant container</param> /// <remarks> /// Default implementation register all API objects /// </remarks> protected virtual void RegisterDependencies( MultitenantContainer container) { container.RegisterAll(TenantId, this.ThisAssembly); }
/// <summary> /// Handles the global application startup event. /// </summary> protected void Application_Start() { // Create the tenant ID strategy. Required for multitenant integration. var tenantIdStrategy = new RequestParameterTenantIdentificationStrategy("tenant"); // Register application-level dependencies and controllers. Note that // we are manually registering controllers rather than all at the same // time because some of the controllers in this sample application // are for specific tenants. var builder = new ContainerBuilder(); builder.RegisterType<HomeController>(); builder.RegisterType<BaseDependency>().As<IDependency>(); // Adding the tenant ID strategy into the container so controllers // can display output about the current tenant. builder.RegisterInstance(tenantIdStrategy).As<ITenantIdentificationStrategy>(); // The service client is not different per tenant because // the service itself is multitenant - one client for all // the tenants and the service implementation switches. builder.Register(c => new ChannelFactory<IMultitenantService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:63578/MultitenantService.svc"))).SingleInstance(); builder.Register(c => new ChannelFactory<IMetadataConsumer>(new WSHttpBinding(), new EndpointAddress("http://localhost:63578/MetadataConsumer.svc"))).SingleInstance(); // Register an endpoint behavior on the client channel factory that // will propagate the tenant ID across the wire in a message header. // On the service side, you'll need to read the header from incoming // message headers to reconstitute the incoming tenant ID. builder.Register(c => { var factory = c.Resolve<ChannelFactory<IMultitenantService>>(); factory.Opening += (sender, args) => factory.Endpoint.Behaviors.Add(new TenantPropagationBehavior<string>(tenantIdStrategy)); return factory.CreateChannel(); }).InstancePerRequest(); builder.Register(c => { var factory = c.Resolve<ChannelFactory<IMetadataConsumer>>(); factory.Opening += (sender, args) => factory.Endpoint.Behaviors.Add(new TenantPropagationBehavior<string>(tenantIdStrategy)); return factory.CreateChannel(); }).InstancePerRequest(); // Create the multitenant container based on the application // defaults - here's where the multitenant bits truly come into play. var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); // Notice we configure tenant IDs as strings below because the tenant // identification strategy retrieves string values from the request // context. To use strongly-typed tenant IDs, create a custom tenant // identification strategy that returns the appropriate type. // Configure overrides for tenant 1 - dependencies, controllers, etc. mtc.ConfigureTenant("1", b => { b.RegisterType<Tenant1Dependency>().As<IDependency>().InstancePerDependency(); b.RegisterType<Tenant1Controller>().As<HomeController>(); }); // Configure overrides for tenant 2 - dependencies, controllers, etc. mtc.ConfigureTenant("2", b => { b.RegisterType<Tenant2Dependency>().As<IDependency>().SingleInstance(); b.RegisterType<Tenant2Controller>().As<HomeController>(); }); // Configure overrides for the default tenant. That means the default // tenant will have some different dependencies than other unconfigured // tenants. mtc.ConfigureTenant(null, b => b.RegisterType<DefaultTenantDependency>().As<IDependency>().SingleInstance()); // Create the dependency resolver using the // multitenant container instead of the application container. DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc)); // Perform the standard MVC setup requirements. AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); }
public void ComponentRegistry_ReturnsRegistryFromCurrentTenantLifetimeScope() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); var scope = mtc.GetCurrentTenantScope(); Assert.AreSame(scope.ComponentRegistry, mtc.ComponentRegistry, "The ComponentRegistry property should behave based on context."); }
private IDependencyResolver RegisterBuilderAutofacMultitenant(HttpConfiguration httpConfiguration) { // From http://docs.autofac.org/en/latest/advanced/multitenant.html#what-is-multitenancy var builder = new ContainerBuilder(); var assembly = Assembly.GetExecutingAssembly(); builder.RegisterModule(new SharedLibModule()); //builder.RegisterType<HelloManager>().As<IHelloManager>().InstancePerRequest(); // TODO: Make configurable builder.RegisterType<RequestParameterStrategy>().As<ITenantIdentificationStrategy>(); // Standard API registration builder.RegisterWebApiFilterProvider(httpConfiguration); builder.RegisterApiControllers(assembly); var container = builder.Build(); //if (container.IsRegistered<ITenantIdentificationStrategy>()) var tenantStrategy = container.Resolve<ITenantIdentificationStrategy>(); var mtc = new MultitenantContainer(tenantStrategy, container); //mtc.ConfigureTenant("A", // b => b.RegisterType<HellloManagerA>().As<IHelloManager>() // .InstancePerDependency()); // Read assembly custom attribute var groupedAssemblies = LoadAssemblies().Select(ass => new { Assembly = ass, TenantAttribute = ass.GetCustomAttribute<TenantAttributeAttribute>() }) .Where(o => o.TenantAttribute != null) .GroupBy(o => o.TenantAttribute.TenantId, o => o.Assembly); foreach (var group in groupedAssemblies) { // TODO: Log tenant being loaded at info level to give a way to debug issue. var grp = group; mtc.ConfigureTenant(grp.Key, cb => { cb.RegisterAssemblyModules(grp.ToArray()); }); } return new AutofacWebApiDependencyResolver(mtc); }
public void Ctor_SetsProperties() { var container = new ContainerBuilder().Build(); var strategy = new StubTenantIdentificationStrategy(); var mtc = new MultitenantContainer(strategy, container); Assert.AreSame(container, mtc.ApplicationContainer, "The application container wasn't set."); Assert.AreSame(strategy, mtc.TenantIdentificationStrategy, "The tenant ID strategy wasn't set."); }
public void ConfigureTenant_RequiresConfiguration() { var builder = new ContainerBuilder(); var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, builder.Build()); Assert.Throws<ArgumentNullException>(() => mtc.ConfigureTenant("tenant1", null)); }
/// <summary> /// Handles the global application startup event. /// </summary> protected void Application_Start(object sender, EventArgs e) { // Create the tenant ID strategy. Required for multitenant integration. var tenantIdStrategy = new OperationContextTenantIdentificationStrategy(); // Register application-level dependencies and service implementations. // Note that we are registering the services as the interface type // because the .svc files refer to the interfaces. We could potentially // use named service types as well. var builder = new ContainerBuilder(); builder.RegisterType<BaseImplementation>().As<IMultitenantService>(); builder.RegisterType<BaseImplementation>().As<IMetadataConsumer>(); builder.RegisterType<BaseDependency>().As<IDependency>(); // Adding the tenant ID strategy into the container so services // can return output about the current tenant. builder.RegisterInstance(tenantIdStrategy).As<ITenantIdentificationStrategy>(); // Create the multitenant container based on the application // defaults - here's where the multitenant bits truly come into play. var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build()); // Notice we configure tenant IDs as strings below because the tenant // identification strategy retrieves string values from the message // headers. // Configure overrides for tenant 1 - dependencies, service implementations, etc. mtc.ConfigureTenant("1", b => { b.RegisterType<Tenant1Dependency>().As<IDependency>().InstancePerDependency(); b.RegisterType<Tenant1Implementation>().As<IMultitenantService>(); b.RegisterType<Tenant1Implementation>().As<IMetadataConsumer>(); }); // Configure overrides for tenant 2 - dependencies, service implementations, etc. mtc.ConfigureTenant("2", b => { b.RegisterType<Tenant2Dependency>().As<IDependency>().SingleInstance(); b.RegisterType<Tenant2Implementation>().As<IMultitenantService>(); b.RegisterType<Tenant2Implementation>().As<IMetadataConsumer>(); }); // Configure overrides for the default tenant. That means the default // tenant will have some different dependencies than other unconfigured // tenants. mtc.ConfigureTenant(null, b => b.RegisterType<DefaultTenantDependency>().As<IDependency>().SingleInstance()); // Multitenant service hosting requires use of a different service implementation // data provider that will allow you to define a metadata buddy class that isn't // tenant-specific. AutofacHostFactory.ServiceImplementationDataProvider = new MultitenantServiceImplementationDataProvider(); // Add a behavior to service hosts that get created so incoming messages // get inspected and the tenant ID can be parsed from message headers. // For multitenancy to work, you need to know for which tenant a // given request is being made. In this case, the incoming message headers // expect to see a string for the tenant ID; if your tenant ID coming // from clients is different, change that here. AutofacHostFactory.HostConfigurationAction = host => host.Opening += (s, args) => host.Description.Behaviors.Add(new TenantPropagationBehavior<string>(tenantIdStrategy)); // Finally, set the host factory application container on the multitenant // WCF host to a multitenant container. This is similar to standard // Autofac WCF integration. AutofacHostFactory.Container = mtc; // Note that the .svc file for your service needs to use the // Autofac.Extras.Multitenant.Wcf.AutofacServiceHostFactory or // Autofac.Extras.Multitenant.Wcf.AutofacWebServiceHostFactory rather // than the standard Autofac host factories. }
public void Dispose_DisposesTenantLifetimeScopes() { var appDependency = new StubDisposableDependency(); var tenantDependency = new StubDisposableDependency(); var builder = new ContainerBuilder(); builder.RegisterInstance(appDependency).OwnedByLifetimeScope(); var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, builder.Build()); mtc.ConfigureTenant("tenant1", b => b.RegisterInstance(tenantDependency).OwnedByLifetimeScope()); // Resolve the tenant dependency so it's added to the list of things to dispose. // If you don't do this, it won't be queued for disposal and the test fails. mtc.Resolve<StubDisposableDependency>(); mtc.Dispose(); Assert.IsTrue(appDependency.IsDisposed, "The application scope didn't run Dispose."); Assert.IsTrue(tenantDependency.IsDisposed, "The tenant scope didn't run Dispose."); }
private void AddTenant(MultitenantContainer mtc, string tenantId) { mtc.ConfigureTenant(tenantId, b => { var options = this.GetMyOptions(tenantId); b.Register(c => new TestDbContext(options.ConnectionString)) .As<TestDbContext>() .InstancePerDependency(); b.ConfigureOptions(options); }); }
public void ConfigureTenant_DoesNotAllowMultipleSubsequentRegistrationsForDefaultTenant() { var builder = new ContainerBuilder(); builder.RegisterType<StubDependency1Impl1>().As<IStubDependency1>(); var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, builder.Build()); mtc.ConfigureTenant(null, b => b.RegisterType<StubDependency1Impl2>().As<IStubDependency1>()); Assert.Throws<InvalidOperationException>(() => mtc.ConfigureTenant(null, b => b.RegisterType<StubDependency2Impl2>().As<IStubDependency2>())); }
public void GetCurrentTenantScope_TenantNotFound() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1", IdentificationSuccess = false }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); var current = mtc.GetCurrentTenantScope(); var tenant = mtc.GetTenantScope(null); Assert.AreSame(tenant, current, "The current scope should be the default tenant scope for the unidentified tenant."); }
public void GetCurrentTenantScope_ChangesByContext() { var strategy = new StubTenantIdentificationStrategy() { TenantId = "tenant1" }; var mtc = new MultitenantContainer(strategy, new ContainerBuilder().Build()); var tenant1a = mtc.GetCurrentTenantScope(); strategy.TenantId = "tenant2"; var tenant2 = mtc.GetCurrentTenantScope(); strategy.TenantId = "tenant1"; var tenant1b = mtc.GetCurrentTenantScope(); Assert.AreSame(tenant1a, tenant1b, "Every time a specific tenant context is seen, the same current scope should be returned."); Assert.AreNotSame(tenant1a, tenant2, "When different tenant contexts are seen, different tenant scopes should be returned."); }
/// <summary> /// Adds tenant-specific registrations to the container /// </summary> /// <param name="container">Autofac multitenant container</param> public void Load(MultitenantContainer container) { container.NotNull(nameof(container)); RegisterDependencies(container); }