Экземпляр на одну и ту же область действия с продолжительностью по умолчанию?

Я хотел бы иметь экземпляр для каждой соответствующей области действия при регистрации в Autofac, но иногда нужно запрашивать экземпляр из глобального контейнера (где нет соответствующей области действия времени жизни). В сценариях, где не существует соответствующей области действия времени жизни, я хочу дать экземпляр верхнего уровня вместо того, чтобы выдавать исключение.

Это возможно?

3 ответа

Решение

Я думаю, что вам лучше расширить Autofac, введя новый вариант жизни. Я взял источники Autofac и немного их модифицировал:

public static class RegistrationBuilderExtensions
{
    public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InstancePerMatchingOrRootLifetimeScope<TLimit, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> builder, params object[] lifetimeScopeTag)
    {
        if (lifetimeScopeTag == null) throw new ArgumentNullException("lifetimeScopeTag");
        builder.RegistrationData.Sharing = InstanceSharing.Shared;
        builder.RegistrationData.Lifetime = new MatchingScopeOrRootLifetime(lifetimeScopeTag);
        return builder;
    }
}

public class MatchingScopeOrRootLifetime: IComponentLifetime
{
    readonly object[] _tagsToMatch;

    public MatchingScopeOrRootLifetime(params object[] lifetimeScopeTagsToMatch)
    {
        if (lifetimeScopeTagsToMatch == null) throw new ArgumentNullException("lifetimeScopeTagsToMatch");

        _tagsToMatch = lifetimeScopeTagsToMatch;
    }

    public ISharingLifetimeScope FindScope(ISharingLifetimeScope mostNestedVisibleScope)
    {
        if (mostNestedVisibleScope == null) throw new ArgumentNullException("mostNestedVisibleScope");

        var next = mostNestedVisibleScope;
        while (next != null)
        {
            if (_tagsToMatch.Contains(next.Tag))
                return next;

            next = next.ParentLifetimeScope;
        }

        return mostNestedVisibleScope.RootLifetimeScope;
    }
}

Просто добавьте эти классы в ваш проект и зарегистрируйте ваш компонент как:

builder.RegisterType<A>.InstancePerMatchingOrRootLifetimeScope("TAG");

Я сам не пробовал, но это должно сработать.

Возможное решение состоит в том, чтобы переопределить регистрацию в дочерней области жизни

Образец:

public enum Scopes
{
    TestScope
}

public class Test
{
   public string Description { get; set; }
}

public class Tester
{
    public void DoTest()
    {
        ContainerBuilder builder = new ContainerBuilder();
        builder.RegisterType<Test>()
            .OnActivating(args => args.Instance.Description = "FromRoot")
            .SingleInstance();
        var container = builder.Build();

        var scope = container.BeginLifetimeScope(Scopes.TestScope, b => b
            .RegisterType<Test>()
            .InstancePerMatchingLifetimeScope(Scopes.TestScope)
            .OnActivating(args => args.Instance.Description = "FromScope"));

        var test1 = container.Resolve<Test>();
        Console.WriteLine(test1.Description); //writes FromRoot

        var test2 = scope.Resolve<Test>();
        Console.WriteLine(test2.Description); //writes FromScope

        Console.ReadLine();
    }
}

Сам корневой контейнер - это область жизни с именем тега: root, определенный в LifetimeScope.RootTag

Итак, вы можете просто сделать:

      using Autofac.Core.Lifetime;

builder.RegisterType<Service>().As<IService>()
    .InstancePerMatchingLifetimeScope(LifetimeScope.RootTag, "foobar");
Другие вопросы по тегам