Как сохранить экземпляр объекта в безымянных / иначе названных областях? - PullRequest
1 голос
/ 06 мая 2019

Как показано в примере запускаемого кода ниже, я хочу создать именованную область, в которой определенный экземпляр объекта разрешается независимо от других безымянных областей, которые создаются после создания объекта.

В отношении документации , найденной здесь :

// You can't resolve a per-matching-lifetime-scope component
// if there's no matching scope.
using(var noTagScope = container.BeginLifetimeScope())
{
  // This throws an exception because this scope doesn't
  // have the expected tag and neither does any parent scope!
  var fail = noTagScope.Resolve<Worker>();
}

В моем случае в приведенном ниже примере родительская область имеет соответствующий тег, но он все еще не работает,Должно ли это быть?

В следующем примере области видимости аккуратны, а родительские области известны. В моем приложении доступен только объект корневого контейнера, поэтому при создании области он всегда находится в контейнере, а не в родительской области.

public class User
{
    public string Name { get; set; }
}

public class SomeService
{
    public SomeService(User user)
    {
        Console.WriteLine($"Injected user is named {user.Name}");
    }
}

class Program
{
    private static IContainer container;
    private const string USER_IDENTITY_SCOPE = "SOME_NAME";

    static void Main(string[] args)
    {
        BuildContainer();
        Run();
        Console.ReadKey();
    }

    private static void BuildContainer()
    {
        ContainerBuilder builder = new ContainerBuilder();
        builder.RegisterType<SomeService>();

        builder.RegisterType<User>().InstancePerMatchingLifetimeScope(USER_IDENTITY_SCOPE);
        container = builder.Build();
    }

    private static void Run()
    {
        using (var outerScope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
        {
            User outerUser = outerScope.Resolve<User>();
            outerUser.Name = "Alice"; // User Alice lives in this USER_IDENTITY_SCOPE

            SomeService someService = outerScope.Resolve<SomeService>(); // Alice


            // Now we want to run a "process" under the identity of a different user
            // Inside of the following using block, we want all services that
            // receive a User object to receive Bob:

            using (var innerSope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE)) 
            {
                User innerUser = innerSope.Resolve<User>();
                innerUser.Name = "Bob"; // We get a new instance of user as expected.  User Bob lives in this USER_IDENTITY_SCOPE

                // Scopes happen in my app that are unrelated to user identity - how do I retain User object despite this?
                // The following is not a USER_IDENTITY_SCOPE -- We still want Bob to be the User object that is resolved:
                using (var unnamedScope = container.BeginLifetimeScope()) 
                {
                    // Crashes. Desired result: User Bob is injected
                    SomeService anotherSomeService = unnamedScope.Resolve<SomeService>(); 
                }
            }
        }
    }
}

Использование Autofac 4.9.2 / .net core 2.2

1 Ответ

0 голосов
/ 07 мая 2019

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

using (var unnamedScope = container.BeginLifetimeScope())

Переключите это, чтобы быть потомком области с именем, и это будет работать.

using (var unnamedScope = innerScope.BeginLifetimeScope())

Я бы также отметил, что вы назвали эти outerScope и innerScope, но innerScope на самом деле не является потомком outerScope, поэтому имена вводят в заблуждение. Технически эти две области равноправны.

  • контейнер
    • innerScope (named)
    • externalScope (named)
    • unnamedScope

Все три являются прямыми потомками контейнера. Если вы подумываете о совместном использовании пользователя с точки зрения иерархии области действия, вам нужно будет создавать дочерние области от родителей, у которых есть дети.

  • контейнер
    • innerScope (named)
    • unnamedScope
    • externalScope (named)
    • unnamedScope

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

Я бы настоятельно рекомендовал не пытаться обойти здесь иерархическую модель. Например, скажем, вы действительно пытаетесь сделать это:

  • контейнер
    • externalScope (named)
    • unnamedScope

Что может выглядеть так:

using(var outerScope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
using(var unnamedScope = container.BeginLifetimeScope())
{
 //...
}

Это в значительной степени то, что у вас есть во фрагменте выше. Единственный общий доступ к этим областям - на уровне контейнера. Если вы попытались разрешить что-то из указанной области и передать ее в одноранговую область, вы рискуете избавиться от вещей из-под себя или других странных проблем, которые трудно устранить. Например, если outerScope утилизируется, но unnamedScope живет, вы можете попасть в беду.

// PLEASE DO NOT DO THIS. YOU WILL RUN INTO TROUBLE.
using(var outerScope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
{
  var user = outerScope.Resolve<User>();
  using(var unnamedScope = container.BeginLifetimeScope(b => b.RegisterInstance(user)))
  {
   //...
  }
}

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

...