在标记范围和未标记范围内解析组件

时间:2019-03-24 12:13:39

标签: c# dependency-injection autofac

我正在尝试为AutoFac中某些标记的生命周期作用域提供不同的服务,但似乎无法掌握它。

我尝试使用Instance per matching lifetime scope, with default?中的自定义生存期,但这没用。

我写了一个测试来说明我要做什么:

[TestMethod]
public void NamedLifetimeTests()
{
    var builder = new ContainerBuilder();
    builder.Register(c => new ChildA()).As<Parent>().InstancePerLifetimeScope();
    builder.Register(c => new ChildB()).As<Parent>().InstancePerMatchingLifetimeScope("system").PreserveExistingDefaults();

    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"))
    {
        var parent = scope.Resolve<Parent>();
        Assert.IsTrue(parent.GetType() == typeof(ChildB));
    }
}

这可能吗?

2 个答案:

答案 0 :(得分:2)

解决方案可能是在创建范围时为生存期范围提供这些自定义服务。使用container.BeginLifetimeScope创建新的生存期范围时,可以提供其他Action<ContainerBuilder>参数来为此特定生存期范围定义一些自定义注册。

因此,对于您的代码,可以将其移至每个作用域的注册,而不是ChildB的全局注册。看起来可能像这样:

[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));
    }
}

编辑: 避免注入生命周期范围是可以理解的。另一个解决方案可能是基于生命周期作用域标签选择适当的实现,类似于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 ...

答案 1 :(得分:0)

以下扩展名可用于自动执行tdragon's answer。它还解决了嵌套范围问题。

<vector 
    android:height="24dp"
    android:tint="?attr/colorControlNormal" 
    android:viewportHeight="24"
    android:viewportWidth="24" 
    android:width="24dp" 
    xmlns:android="http://schemas.android.com/apk/res/android">
    <group
        android:scaleX="0.5"
        android:scaleY="0.5"
        android:pivotX="12"
        android:pivotY="12">
        <path
        android:fillColor="@android:color/white"
        android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
    </group>
</vector>

用法

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;
        }
    }
}

结果

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>());
    }
}