asp.net-mvc - Autofac 生命周期管理

标签 asp.net-mvc autofac lifetime

我正在开发一个支持外部插件的 ASP.NET MVC 项目,现在,我正在从 Unity 迁移到 Autofac,我需要包装 Autofac 的生命周期对象,这样插件就不必在 Unity 中引用它了可以这样做。

public sealed class UnityScopeFactory : IDependencyScopeFactory
{
    private HttpRequestScope _httpRequest;

    private SingletonScope _singleton;

    private TransientScope _transient;

    public IDependencyScope HttpRequest()
    {
        return _httpRequest ?? (_httpRequest = new HttpRequestScope());
    }

    public IDependencyScope Singleton()
    {
        return _singleton ?? (_singleton = new SingletonScope());
    }

    public IDependencyScope Transient()
    {
        return _transient ?? (_transient = new TransientScope());
    }

    private class HttpRequestScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new HttpPerRequestLifetimeManager();
        }
    }

    private class SingletonScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new ContainerControlledLifetimeManager();
        }
    }

    private class TransientScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new TransientLifetimeManager();
        }
    }
}

我在 Autofac 中做了类似的事情,但我不确定这是否是正确的方法,我查看了 Autofac 的 RegistrationBuilder,它是(不幸的)内部的,我想出了这个。
public class AutofacScopeFactory : IDependencyScopeFactory
{
    private HttpRequestScope _httpRequest;

    private SingletonScope _singleton;

    private TransientScope _transient;

    public IDependencyScope HttpRequest()
    {
        return _httpRequest ?? (_httpRequest = new HttpRequestScope());
    }

    public IDependencyScope Singleton()
    {
        return _singleton ?? (_singleton = new SingletonScope());
    }

    public IDependencyScope Transient()
    {
        return _transient ?? (_transient = new TransientScope());
    }

    private class HttpRequestScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new CurrentScopeLifetime();
        }
    }

    private class SingletonScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new RootScopeLifetime();
        }
    }

    private class TransientScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new CurrentScopeLifetime();
        }
    }
}

另外,在我让它工作之后,我如何使用将它传递给 ContainerBuilder?

在 Unity 中,我可以做这样的事情。
public sealed class UnityDependencyContainer : IDependencyContainer
{
    private readonly IUnityContainer _container;

    public UnityDependencyContainer()
    {
        _container = new UnityContainer()
    }

    public void Register<TContract, TImplementation>(IDependencyScope scope) where TImplementation : TContract
    {
        LifetimeManager manager = scope.CreateScope() as LifetimeManager;

        if (manager != null)
        {
            _container.RegisterType<TContract, TImplementation>(manager);
        }
    }
}

如何将 IComponentLifetime 的实例传递给方法链?这是死胡同吗?
public class AutofacContainer : IDependencyContainer
{
    private static readonly ContainerBuilder Builder;

    static AutofacContainer()
    {
        Builder = new ContainerBuilder();
    }

    public void RegisterType<TContract, TImplementation>(IDependencyScope scope) where TImplementation : TContract
    {
        IComponentLifetime manager = scope.CreateScope() as IComponentLifetime;

        if (manager != null)
        {
            Builder.RegisterType<TImplementation>().As<TContract>();
        }
    }
}

最佳答案

Autofac 并没有按照您描述的方式完全分离范围,所以 您可能正在尝试在圆孔中安装方钉 .

Autofac 范围更具层次性。任何生命周期范围都可以产生一个子 transient 范围。例如,您可能会看到...

  • 容器/根生命周期
  • HttpRequest 范围
  • 小型任务特定的 transient 范围

  • 您可以“标记”范围并将组件注册到特定的命名/标记范围 - 这就是 HttpRequest 范围的工作方式。它被一个特殊的标识符“标记”。

    当您解析对象时,它决定了哪个生命周期范围拥有它。解析发生在最嵌套的范围内。在上面的层次结构中,您从特定于小型任务的 transient 范围解析项目,无论它们是单例、请求范围还是其他。当单例被解析时,它将搜索生命周期范围堆栈并自动将对象的“所有权”分配给根生命周期范围。当每个请求项得到解决时,它会在堆栈中搜索具有特殊“HTTP 请求”标识符的生命周期范围,并在那里分配所有权。工厂范围的项目在当前生命周期范围内解析。

    注意:该讨论是对其工作方式的严重过度简化。 There is documentation explaining the lifetime scope mechanism on the Autofac site.

    重点是,我在上述设计中看到了一些与 Autofac 做事的方式并没有真正“融合”的东西。

    DependencyScopeFactory 无法创建自己的 transient 或 HttpRequest 范围。 有特定的生命周期管理组件开始和结束 HttpRequest 范围,因此您需要使用它们;没有“全局” transient 范围,所以你不能真的只创建一个。

    HttpRequest 范围,假设您使用的是 MVC,看起来更像......
    public ILifetimeScope HttpRequestScope
    {
      get { return AutofacDependencyResolver.Current.RequestLifetime; }
    }
    

    transient 范围没有类似物,因为它的使用应该是内联的:
    using(var transientScope = parentScope.BeginLifetimeScope())
    {
      // Do stuff and resolve dependencies using the transient scope.
      // The IDisposable pattern here is important so transient
      // dependencies will be properly disposed at the end of the scope.
    }
    

    当您注册组件时,您不会将它们注册到“生命周期范围内”。 您实际上将它们注册到组件注册表中,并且组件注册的一部分包括有关组件生命周期的所有权信息,一旦它被解析。
    var builder = new ContainerBuilder();
    
    // This component is factory-scoped and will be "owned" by whatever
    // lifetime scope resolves it. You can resolve multiple of these
    // in a single scope:
    builder.RegisterType<FirstComponent>().As<ISomeInterface>();
    
    // This component is a singleton inside any given lifetime scope,
    // but if you have a hierarchy of scopes, you'll get one in each
    // level of the hierarchy.
    builder.RegisterType<SecondComponent>().InstancePerLifetimeScope();
    
    // This component will be a singleton inside a specifically named
    // lifetime scope. If you try to resolve it in a scope without that
    // name, it'll search up the scope stack until it finds the scope
    // with the right name. If no matching scope is found - exception.
    builder.RegisterType<ThirdComponent>().InstancePerMatchingLifetimeScope("scopename");
    
    // This is a per-HTTP-request component. It's just like the
    // above InstancePerMatchingLifetimeScope, but it has a special
    // tag that the web integration knows about.
    builder.RegisterType<FourthComponent>().InstancePerHttpRequest();
    

    如果您尝试创建与容器/注册无关的接口(interface),则不需要“生命周期范围管理器” - 相反,您需要传递一些指示预期生命周期范围的参数,并根据传入的参数执行适当的注册语法(如上)。

    再次,I'd recommend you check out that documentation .

    此外,如果您使用 Unity, Autofac 确实有一个 Enterprise Library Configurator 包 它允许您以 Unity 样式配置 Autofac(因为这就是 EntLib 喜欢做事的方式)。这可能是要检查的东西。

    如果您根本不需要使用 Unity 语法... 我建议只用原生 Autofac 方式做事。 试图让一个容器看起来和表现得像另一个容器是一项非常痛苦的工作。

    假设您的插件位于单独的程序集中或其他任何地方,您可以轻松利用一些不错的程序集扫描语法以及 Autofac 模块并以这种方式连接您的插件。

    关于asp.net-mvc - Autofac 生命周期管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13736771/

    相关文章:

    c# - 使用 Autofac 将 WebAPI UrlHelper 注入(inject)服务

    c# - Autofac - 动态解析带参数的组件

    asp.net-mvc - 如何将身份成员资格与现有数据库(n 层)一起使用

    jquery - 无法在 jQuery 中调用 @Html.Action()

    c# - 将数据表更新为 Entity Framework 中的多个表

    autofac - 无法解析参数 'MediatR.ServiceFactory serviceFactory'(带有 Autofac 的 MediatR)

    generics - 重写一个函数以接受AsRef <Path>而不是&Path

    rust - 为什么作为参数传递的特征对象的生命周期需要更高等级的特征界限,而结构不需要?

    enums - Rust 中的显式生命周期错误

    ASP.Net MVC - 停止自动完成密码