如何在未命名/不同命名范围内保留对象实例?

时间:2019-05-06 15:41:55

标签: autofac

如下面的可运行代码示例所示,我想创建一个命名作用域,其中对象的某个实例被解析,而与创建对象后创建的其他未命名作用域无关。

关于documentation found here

// 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 :(得分:0)

在您的示例中,您是从容器而不是具有名称的父级启动未命名的作用域:

using (var unnamedScope = container.BeginLifetimeScope())

将其切换为具有名称的作用域的子代,它将起作用。

using (var unnamedScope = innerScope.BeginLifetimeScope())

我还要注意,您已经为outerScopeinnerScope命名,但是innerScope实际上不是outerScope的子代,因此名称具有误导性。从技术上讲,这两个范围是对等的。

  • 容器
    • innerScope(命名)
    • outerScope(命名)
    • unnamedScope

这三个都是容器的直接子代。如果您考虑根据范围层次结构共享用户,则需要从有孩子的父母那里创建孩子范围。

  • 容器
    • innerScope(命名)
    • unnamedScope
    • outerScope(命名)
    • unnamedScope

您会注意到inner和external仍然是对等体-不能有同名的父母和孩子,因此给定inner和external都被命名,除了容器,它们永远不会共享相同的层次结构

我会强烈建议,不要在这里绕过分层模型。例如,假设您确实在尝试这样做:

  • 容器
    • outerScope(命名)
    • 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)))
  {
   //...
  }
}

这是一个坏消息,有待解决,从奇怪的处置问题到您认为应该不共享相同依赖关系的事物。但是,您知道,我们可以给您枪,这取决于您自己不要用枪射击自己。