2
\$\begingroup\$

Some of the classes in my multi-tenant Asp.Net Core application depend on database repositories which in turn depend on a delegate called GetCurrentTenantKey.

In the normal request pipeline, the GetCurrentTenantKey delegate will be resolved to a Func that returns the tenant key found in the current users auth token.

But then I have a few background tasks that also need access to repositories, and in those cases, the tenant key doesn't come from any "current user", rather I'd like to provide it then and there. I thought the best solution would be to manually bind GetCurrentTenantKey to a local Func, but I don't want to affect the "normal" dependency tree in any way.

This is the class you'll use to create a scope:

public class IsolatedServiceScopeFactory : IServiceScopeFactory
{
    private readonly IServiceCollection _services;

    public IsolatedServiceScopeFactory(IServiceCollection services)
    {
        _services = services;
    }

    public IServiceScope CreateScopeWithOverrides(Func<IServiceCollection, IServiceCollection> overrides)
    {
        var detachedCollection = new ImmutableServiceCollection(_services);

        var collectionWithOverrides = overrides(detachedCollection);

        return collectionWithOverrides.BuildServiceProvider().CreateScope();
    }

    public IServiceScope CreateScope() => _services.BuildServiceProvider().CreateScope();
}

It's intended to be used like this:

using (var serviceScope = _serviceScopeFactory.CreateScopeWithOverrides(x => x
    .AddScoped<GetCurrentTenantKey>(y => () => "any string")
    .AddSingleton<SomeOtherOverride>()
))
{
    // This will be resolved using my "overridden" version of GetCurrentTenantKey
    var myRepository = serviceScope.ServiceProvider.GetRequiredService<IRepository<SomeType>>();
}

The ImmutableServiceCollection class is quite simple:

public class ImmutableServiceCollection : IServiceCollection
{
    private ImmutableList<ServiceDescriptor> _services;

    public ImmutableServiceCollection(IEnumerable<ServiceDescriptor> services)
    {
        _services = services.ToImmutableList();
    }

    public IEnumerator<ServiceDescriptor> GetEnumerator()
    {
        return _services.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Add(ServiceDescriptor item)
    {
        _services = _services.Add(item);
    }

    public void Clear()
    {
        _services = _services.Clear();
    }

    public bool Contains(ServiceDescriptor item)
    {
        return _services.Contains(item);
    }

    public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
    {
        _services.CopyTo(array, arrayIndex);
    }

    public bool Remove(ServiceDescriptor item)
    {
        var removed = _services.Contains(item);
        _services = _services.Remove(item);

        return removed;
    }

    public int Count => _services.Count;

    public bool IsReadOnly { get; } = true;

    public int IndexOf(ServiceDescriptor item)
    {
        return _services.IndexOf(item);
    }

    public void Insert(int index, ServiceDescriptor item)
    {
        _services = _services.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        _services = _services.RemoveAt(index);
    }

    public ServiceDescriptor this[int index]
    {
        get => _services[index];
        set => _services = _services.SetItem(index, value);
    }
}

It seems to work, but I'm not sure if I've maybe went about this the wrong way somehow?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.