【问题标题】:How to retain an object instance across unnamed/differently named scopes?如何在未命名/不同命名的范围内保留对象实例?
【发布时间】:2019-05-06 15:41:54
【问题描述】:

如下面的可运行代码示例所示,我想创建一个命名范围,其中解析对象的某个实例,而不考虑在创建对象后创建的其他未命名范围。

关于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

【问题讨论】:

    标签: autofac


    【解决方案1】:

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

    using (var unnamedScope = container.BeginLifetimeScope())

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

    using (var unnamedScope = innerScope.BeginLifetimeScope())

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

    • 容器
      • innerScope(命名)
      • outerScope(命名)
      • 未命名范围

    这三个都是容器的直接子级。如果您考虑在范围层次结构方面共享用户,则需要从有子级的父级创建子级域。

    • 容器
      • innerScope(命名)
      • 未命名范围
      • outerScope(命名)
      • 未命名范围

    您会注意到内部和外部仍然是对等的 - 您不能拥有同名的父子和子,因此如果内部和外部都被命名,它们将永远不会共享相同的层次结构,除了容器.

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

    • 容器
      • outerScope(命名)
      • 未命名范围

    可能看起来像这样:

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

    这几乎就是您在上面的 sn-p 中所拥有的。这些范围的唯一共同共享是在容器级别。如果您尝试从指定范围内解决某些问题并将其传递到对等范围,则您可能会冒着被您处理掉的风险或其他难以解决的奇怪问题。就像如果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)))
      {
       //...
      }
    }
    

    这是等待发生的坏消息,从奇怪的处置问题到您认为应该共享同一组依赖项的事情不共享。但是,你知道,我们可以把枪给你,你要不要用它来打自己的脚。

    【讨论】:

    • 谢谢 Travis - 你的回答与"In Autofac, the innermost scope is always the container" 有什么关系?正如我的问题中所述,我不会传递范围或存储它。我需要维护一堆范围还是 Autofac 可以提供的东西?
    • 不同的问题,不同的答案。如果您创建范围,则需要跟踪它们。权力越大,责任越大。
    • > 你知道,我们可以给你枪,这取决于你不要用它来打自己的脚。拥有权利的同时也被赋予了重大的责任。仅供参考,我不需要你这个。这不是答案的必要部分。
    • 很抱歉你有这种感觉。我把重要的警告放在了那里,不仅是为了问这个问题的人,也是为了未来的读者。有很多像这样的问题出现,有时会问如何做一些不是很好的想法。我试图帮助回答他们,但让人们了解他们何时进入可能对自己造成的伤害多于好处的领域非常重要。这是其中之一,因此警告人们似乎是合适的。
    • 考虑到我们为 free 编写和支持 Autofac 可能也很好。您将从该项目的一位完全无偿作者那里获得免费支持,他们会在他们的空闲时间回答您的问题,而不是从事更多有趣的爱好或与家人共度时光。如果您不喜欢支持的方式,我们提供退款保证。 :)
    猜你喜欢
    • 2015-01-25
    • 2011-12-30
    • 2012-01-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多