Search code examples
c#autofac

Set the active tenant on a resolved child scope


Is there a way to set the active tenant on a child scope? I assumed it would have a global affect but it does not after the child scope (ILifetimeScope) has been resolved.

Dependencies

<package id="Autofac" version="4.9.2" targetFramework="net471" />
<package id="Autofac.Multitenant" version="4.2.0" targetFramework="net471" />

Reproduction

Reproduction written using a .net framework console application and the above listed dependencies.

using Autofac;
using Autofac.Multitenant;

namespace ConsoleAutoFacTenants
{
    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<GeneralReader>().As<IReader>().InstancePerDependency();
            var appContainer = builder.Build();

            var tenantIdentifier = new AutomationTenantStrategy();
            var mtc = new MultitenantContainer(tenantIdentifier, appContainer);

            mtc.ConfigureTenant("1", b => b.RegisterType<SpecificReader>().As<IReader>().InstancePerDependency());

            // expected
            var reader1 = mtc.Resolve<IReader>();
            System.Diagnostics.Debug.Assert(reader1.Name == "General");

            // unexpected result in debug.assert, assumed that reader2 would resolve type SpecificReader
            var childScoped = mtc.BeginLifetimeScope();
            tenantIdentifier.TenantId = "1";
            var reader2 = childScoped.Resolve<IReader>();
            System.Diagnostics.Debug.Assert(reader2.Name == "Specific");
        }
    }
    internal sealed class AutomationTenantStrategy : ITenantIdentificationStrategy
    {
        public object TenantId { get; set; }
        public bool TryIdentifyTenant(out object tenantId)
        {
            var activeTenant = this.TenantId;
            if (TenantId == null)
            {
                tenantId = null;
                return false;
            }
            tenantId = activeTenant;
            return true;
        }
    }
    public interface IReader
    {
        string Name { get; }
    }
    public sealed class GeneralReader : IReader
    {
        public string Name => "General";
    }
    public sealed class SpecificReader : IReader
    {
        public string Name => "Specific";
    }
}

Solution

  • When you resolve a ILifetimeScope from a scope it will return itself and not create a child lifetimescope.

    This line :

    var childScoped = mtc.Resolve<ILifetimeScope>();
    

    should be replaced by

    var childScoped = mtc.BeginLifetimeScope();
    

    When a new LifetimeScope is created the multitenant module will set the tenantId to the scope. In your sample you have to "change" the tenantId before the creation of the child LifetimeScope

    tenantIdentifier.TenantId = "1";
    var childScoped = mtc.BeginLifetimeScope();
    

    The resolved IReader will then be the one specified for the tenant.