Autofac optional/lazy 依赖项
Autofac optional/lazy dependencies
如果我将 Lazy 放入我的对象的构造函数中,并且 X 未在容器中注册,我会遇到依赖项解析异常。
为什么我会收到此异常?我不喜欢它,因为我不能在运行时选择组件。示例用例:
class Controller
{
public Controller(Lazy<A> a, Lazy<B> b) { /* (...) */ }
Lazy<A> a;
Lazy<B> b;
public IActionResult Get(){
if(someConfig)
return Json(a.Value.Execute());
else
return Json(b.Value.Execute());
}
}
为此,我需要注册组件 A 和 B。即使从未使用过 B,我的程序也会失败。我希望 B 是可选的,并且仍然由 autofac 管理。
如果我有组件列表并且只想使用一个组件,这将是一个更大的问题。例如:
class Controller
{
Controller(IEnumerable<Component> components) { /* (...) */ }
IActionResult Get()
{
return components.First(n => n.Name == configuredComponent).Execute();
}
}
我不再收到未注册的异常,但仍然构建了所有内容。而且用起来会很别扭
如果您添加对 Lazy<T>
组件的引用,Autofac 必须(基本上)知道如何创建内部函数,运行 如果您想解析它,即使你不解决它。
基本上,它需要能够在内存中创建这个:
var lazy = new Lazy<T>(() => scope.Resolve<T>());
Autofac 要求注册您要解决的所有问题。它不允许您动态注册东西——它必须是明确的。所以你尝试做的事情不会起作用(如你所见)。
而是使用一个接口和该接口的两个不同实现。根据您的配置值更改注册。
var builder = new ContainerBuilder();
if(someConfig)
{
builder.RegisterType<A>().As<IService>();
}
else
{
builder.RegisterType<B>().As<IService>();
}
然后在你的控制器中,注入接口而不是具体的 class。
public MyController(Lazy<IService> service)
您还可以使用其他选项,例如使用 metadata for components or keyed services。例如,您可以根据您的配置添加一些元数据并使用它进行解析。
builder.RegisterType<A>()
.As<IService>()
.WithMetadata("Name", "a");
builder.RegisterType<B>()
.As<IService>()
.WithMetadata("Name", "b");
在控制器中,您会得到它们的字典:
public MyController(IEnumerable<Meta<IService>> services)
{
var service = services.First(s => s.Metadata["Name"].Equals(someConfig);
}
这是一个非常简短的示例,但是 the docs show a lot more。
无论如何,界面确实是您成功的关键。如果您只是使用具体的 classes,无论您是否使用它们都必须注册。
如果我将 Lazy 放入我的对象的构造函数中,并且 X 未在容器中注册,我会遇到依赖项解析异常。
为什么我会收到此异常?我不喜欢它,因为我不能在运行时选择组件。示例用例:
class Controller
{
public Controller(Lazy<A> a, Lazy<B> b) { /* (...) */ }
Lazy<A> a;
Lazy<B> b;
public IActionResult Get(){
if(someConfig)
return Json(a.Value.Execute());
else
return Json(b.Value.Execute());
}
}
为此,我需要注册组件 A 和 B。即使从未使用过 B,我的程序也会失败。我希望 B 是可选的,并且仍然由 autofac 管理。
如果我有组件列表并且只想使用一个组件,这将是一个更大的问题。例如:
class Controller
{
Controller(IEnumerable<Component> components) { /* (...) */ }
IActionResult Get()
{
return components.First(n => n.Name == configuredComponent).Execute();
}
}
我不再收到未注册的异常,但仍然构建了所有内容。而且用起来会很别扭
如果您添加对 Lazy<T>
组件的引用,Autofac 必须(基本上)知道如何创建内部函数,运行 如果您想解析它,即使你不解决它。
基本上,它需要能够在内存中创建这个:
var lazy = new Lazy<T>(() => scope.Resolve<T>());
Autofac 要求注册您要解决的所有问题。它不允许您动态注册东西——它必须是明确的。所以你尝试做的事情不会起作用(如你所见)。
而是使用一个接口和该接口的两个不同实现。根据您的配置值更改注册。
var builder = new ContainerBuilder();
if(someConfig)
{
builder.RegisterType<A>().As<IService>();
}
else
{
builder.RegisterType<B>().As<IService>();
}
然后在你的控制器中,注入接口而不是具体的 class。
public MyController(Lazy<IService> service)
您还可以使用其他选项,例如使用 metadata for components or keyed services。例如,您可以根据您的配置添加一些元数据并使用它进行解析。
builder.RegisterType<A>()
.As<IService>()
.WithMetadata("Name", "a");
builder.RegisterType<B>()
.As<IService>()
.WithMetadata("Name", "b");
在控制器中,您会得到它们的字典:
public MyController(IEnumerable<Meta<IService>> services)
{
var service = services.First(s => s.Metadata["Name"].Equals(someConfig);
}
这是一个非常简短的示例,但是 the docs show a lot more。
无论如何,界面确实是您成功的关键。如果您只是使用具体的 classes,无论您是否使用它们都必须注册。