Autofac生命周期管理

6

我正在从Unity迁移到Autofac,为支持外部插件的ASP.NET MVC项目中,我需要包装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>();
        }
    }
}
1个回答

10
Autofac的作用域并不像你所描述的那样完全分离,因此你可能试图把方形钉子塞进圆孔中
Autofac的作用域更具层次性。任何生存期作用域都可以生成一个子暂时作用域。例如,您可能会看到...
- 容器/根生存期
- HttpRequest 作用域
- 小型任务特定的短暂作用域
您可以“标记”一个作用域,并将组件注册到特定的命名/标记作用域 - 这就是HttpRequest作用域的工作原理。它会被“标记”为特殊的标识符。
当解析对象时,就确定了它所属的生存期作用域。从最嵌套的作用域开始解析。在上面的层次结构中,无论单例、请求作用域还是其他作用域,您都可以从小型任务特定的短暂作用域中解析项目。当解析单例时,它将向上搜索生存期作用域堆栈并自动将对象的“所有权”分配给根生存期。当解析每个请求的项时,它会在堆栈上搜索带有特殊的“HTTP请求”标识符的生存期作用域,并将所有权分配到那里。工厂作用域项在当前生存期作用域中解析。 注意:该讨论是如何工作的严重简化。Autofac网站上有关于生存期作用域机制的文档。 重点是,我在上面的设计中看到了一些不符合Autofac做事方式的东西。 DependencyScopeFactory无法创建自己的短暂或HttpRequest作用域。 有特定的生命周期管理组件开始和结束HttpRequest作用域,因此您需要使用这些组件;没有“全局”瞬时作用域,因此您实际上不能创建一个。如果您正在使用MVC,则HttpRequest作用域将更像...
public ILifetimeScope HttpRequestScope
{
  get { return AutofacDependencyResolver.Current.RequestLifetime; }
}

没有临时作用域的类比,因为它的使用应该是内联的:

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();
如果您正在尝试创建一个与容器/注册无关的接口,那么它不需要“lifetime scope manager”,相反,您需要传递一些参数来指示预期的生存期范围,并根据传入的参数执行适当的注册语法(上面的语法)。 同样,我建议您查看该文档 另外,如果您使用的是Unity,Autofac确实有一个Enterprise Library Configurator包,允许您以Unity风格配置Autofac(因为EntLib喜欢以这种方式处理事物)。这可能是值得一看的东西。
如果您根本不需要使用Unity语法... 我建议您采用本机Autofac方法来完成任务。尝试使一个容器看起来和另一个容器一样是一项相当痛苦的工作。
假设您的插件在单独的程序集中,您可以轻松地利用一些不错的程序集扫描语法以及Autofac模块,并通过这种方式连接您的插件。

哇!非常感谢您抽出时间,真的很感激。在这个阶段更改真的很麻烦,我非常渴望尝试使用Autofac,但如果不能使用它,因为它的工作方式不同,我将不得不使用另一个容器或者继续使用当前的容器,因为目前更改的成本太高了。 - eyalalonn
很好的解释!+1! - Cristian E.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接