Resolve component in both tagged scope and untagged scope

前端 未结 2 1842
轻奢々
轻奢々 2021-01-23 04:01

I\'m try to supply a different service to some tagged lifetime scopes in AutoFac, but can\'t seem to get the hang of it.

I\'ve tried using the custom lifetime from Insta

相关标签:
2条回答
  • 2021-01-23 04:47

    The following extension can be used to automate tdragon's answer. It also solves the nested scope problem.

    public static class RegistrationBuilderExtensions
    {
        public static IRegistrationBuilder<TLimit, SimpleActivatorData, SingleRegistrationStyle>
            RegisterScopeResolver<TLimit>(this ContainerBuilder containerBuilder)
        {
            IRegistrationBuilder<TLimit, SimpleActivatorData, SingleRegistrationStyle> defineServiceScopes =
                containerBuilder.Register(componentContext =>
                {
                    var scope = componentContext.Resolve<ILifetimeScope>();
                    var registrations = componentContext.ComponentRegistry.RegistrationsFor(new TypedService(typeof(TLimit))).ToList();
                    Type activatorLimitType = registrations.Where(x => x.Lifetime is MatchingScopeLifetime).SingleOrDefault(x =>
                            (x.Lifetime as MatchingScopeLifetime).TagsToMatch.Contains(scope.Tag))?.Activator.LimitType;
    
                    if (activatorLimitType == null)
                    {
                        activatorLimitType = registrations.Single(x =>
                            {
                                return x.Lifetime.GetType() != typeof(MatchingScopeByFirstOccurenceLifetime<TLimit>) &&
                                       x.Lifetime.GetType() != typeof(MatchingScopeLifetime);
                            })
                            .Activator
                            .LimitType;
                    }
                    return (TLimit) componentContext
                        .Resolve(activatorLimitType);
                });
            defineServiceScopes.RegistrationData.Sharing = InstanceSharing.Shared;
            defineServiceScopes.RegistrationData.Lifetime = new MatchingScopeByFirstOccurenceLifetime<TLimit>();
            return defineServiceScopes;
        }
    
        private class MatchingScopeByFirstOccurenceLifetime<TLimit> : IComponentLifetime
        {
            public ISharingLifetimeScope FindScope(ISharingLifetimeScope mostNestedVisibleScope)
            {
                if (mostNestedVisibleScope == null)
                    throw new ArgumentNullException(nameof(mostNestedVisibleScope));
    
                var next = mostNestedVisibleScope;
                while (next != null)
                {
                    if (next.ComponentRegistry
                        .RegistrationsFor(new TypedService(typeof(TLimit)))
                        .Select(x => x.Lifetime)
                        .OfType<MatchingScopeLifetime>()
                        .Any(x => x.TagsToMatch.Contains(next.Tag)))
                        return next;
                    next = next.ParentLifetimeScope;
                }
                return mostNestedVisibleScope.RootLifetimeScope;
            }
        }
    }
    

    Usage

    ContainerBuilder containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterType<BaseA>().As<IMyBase>().InstancePerMatchingLifetimeScope("A").AsSelf();
    containerBuilder.RegisterType<BaseB>().As<IMyBase>().InstancePerMatchingLifetimeScope("B").AsSelf();
    containerBuilder.RegisterScopeResolver<IMyBase>();
    
    var container = containerBuilder.Build();
    using (var lifetimeScope = container.BeginLifetimeScope("A"))
    {
        Console.WriteLine(lifetimeScope.Resolve<IMyBase>());
        using (var nestedLifetimeScope = lifetimeScope.BeginLifetimeScope())
        {
            Console.WriteLine(nestedLifetimeScope.Resolve<IMyBase>());
        }
    }
    
    using (var lifetimeScope = container.BeginLifetimeScope("B"))
    {
        Console.WriteLine(lifetimeScope.Resolve<IMyBase>());
        using (var nestedLifetimeScope = lifetimeScope.BeginLifetimeScope())
        {
            Console.WriteLine(nestedLifetimeScope.Resolve<IMyBase>());
        }
    }
    

    Result

    BaseA Id:69fc4258-6920-42a6-8404-94d11ce46064
    BaseA Id:69fc4258-6920-42a6-8404-94d11ce46064
    BaseB Id:53d45071-5810-4358-ae87-bf250f5e3f02
    BaseB Id:53d45071-5810-4358-ae87-bf250f5e3f02
    
    0 讨论(0)
  • 2021-01-23 04:48

    The solution could be to provide these custom services for the lifetime scope when you create the scope. When creating a new lifetime scope with container.BeginLifetimeScope you can provide additional Action<ContainerBuilder> parameter to define some custom registrations for this particular lifetime scope.

    So, for your code, instead of global registration for ChildB, you could move it to per-scope registration. It could look something like that:

    [TestMethod]
    public void NamedLifetimeTests()
    {
        var builder = new ContainerBuilder();
        builder.Register(c => new ChildA()).As<Parent>().InstancePerLifetimeScope();    
    
        var container = builder.Build();
        using (var scope = container.BeginLifetimeScope())
        {
            var parent = scope.Resolve<Parent>();
            Assert.IsTrue(parent.GetType() == typeof(ChildA));
        }
    
        using (var scope = container.BeginLifetimeScope("system"), cb => { cb.RegisterType<ChildB>().As<Parent>(); }))
        {
            var parent = scope.Resolve<Parent>();
            Assert.IsTrue(parent.GetType() == typeof(ChildB));
        }
    }
    

    EDIT: Avoiding injecting lifetime scope is understandable. Another solution could be picking proper implementation, based on lifetime scope tag, similar to selection of implementation based on parameter, described in docs:

    // omitted ...
    var builder = new ContainerBuilder();
    builder.Register<Parent>(c =>
        {
            var currentScope = c.Resolve<ILifetimeScope>();
            if (currentScope.Tag?.ToString() == "system")
            {
                return new ChildB();
            }
    
            return new ChildA();
        }).InstancePerLifetimeScope();   
    
    var container = builder.Build();
    // omitted ...
    
    0 讨论(0)
提交回复
热议问题