我正在开发一个支持外部插件的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>();
}
}
}
答案 0 :(得分:10)
Autofac不会像您所描述的那样将范围分开,所以您可能试图将方形钉固定在圆孔中。
Autofac范围更具层次性。任何生命周期范围都可以产生子瞬态范围。例如,您可能会看到......
您可以“标记”范围并将组件注册到特定的命名/标记范围 - 这就是HttpRequest范围的工作方式。它被标记为“特殊标识符”。
当您解析对象时,它确定哪个生命周期范围拥有它。解析发生在最嵌套的范围内。在上面的层次结构中,您可以从小任务特定的瞬态范围中解析项目,无论它们是单例,请求范围还是其他。单例解析后,它将搜索生命周期范围堆栈并自动将对象的“所有权”分配给根生存期范围。当每个请求项被解析时,它会使用特殊的“HTTP请求”标识符在堆栈中搜索生命周期范围,并在那里分配所有权。工厂范围的项目在当前的生命周期范围内得到解决。
注意:该讨论过于简单化了。 There is documentation explaining the lifetime scope mechanism on the Autofac site.
重点是,我看到上面设计中的一些东西并没有像Autofac那样“jive”。
DependencyScopeFactory无法创建自己的瞬态或HttpRequest范围。有特定的生命周期管理组件可以启动和结束HttpRequest范围,因此您需要使用它们;没有“全局”瞬态范围,所以你不能真正创建一个。
HttpRequest范围,假设您正在使用MVC,看起来更像......
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();
如果您正在尝试创建容器/注册不可知接口,则不需要“生命周期范围管理器” - 相反,您需要传递一些指示预期生命周期的参数范围并根据传入的参数执行适当的注册语法(上面)。
再次,I'd recommend you check out that documentation。
此外,如果您使用的是Unity, Autofac确实有一个Enterprise Library Configurator软件包,它允许您以Unity样式配置Autofac(因为这就是EntLib喜欢做的事情)。这可能是值得一试的。
如果您根本不需要使用Unity语法...... 我建议您只需要使用原生Autofac方式进行操作。尝试使一个容器看起来像另一个容器一样一个非常痛苦的努力。
假设您的插件位于不同的程序集或其他任何组件中,您可以轻松利用一些漂亮的程序集扫描语法和Autofac模块,并以此方式连接插件。