对于 class A 和从属 class B 实例,每个字符串 ctor 参数值的单例
Singleton per string ctor parameter value, for both class A and dependent class B instances
我需要创建一个实例,不是基于类型或生命周期范围声明,而是基于构造函数的字符串参数的值。对于另一个 class 的依赖实例,我也需要同样的效果,在其自己的无关构造器中使用相同的字符串。
class A {
public A(string appId, B b) {}
}
class B {
public B(string appId) {}
}
在上面的示例中,我需要为 appId 的唯一值创建一个 A 单例和一个 B 单例。
我可以使用 TypedParameter 解析 A 和 B,但是我无法弄清楚每个 appId 值的单例部分。我只尝试了 A 来简化(不涉及依赖 B)。我查看了文档中的 Keyed、Indexed 等,但 none 似乎适合每个用户定义的键值单例,除非我编写自己的 Register lambda,它使用我自己的唯一 appId 内存缓存键。
Autofac 是否有内置的简洁方式来强制执行此操作?
这个问题不会有很好的解决方案,因为依赖注入通常是基于类型,而不是参数值。这与基于参数值为 Web 应用程序设置缓存不同。更具体地说,Autofac 没有任何可以帮助您的“内置”内容。 (据我所知,任何其他 DI 框架也没有。)
您可能需要做的是创建一个小型工厂来为您进行缓存并使用它。
这是一个例子,我没有编译也没有测试,但我突然想到让你畅通无阻。
首先,您需要为您的 B
class 创建一个小工厂。
public class BFactory
{
private ConcurrentDictionary<string, B> _cache = new ConcurrentDictionary<string, B>();
public B GetB(string appId)
{
return this._cache.GetOrAdd(
appId,
a => new B(a));
}
}
现在您将 BFactory
注册为单例,这将使您获得想要的 one-instance-per-app-ID 行为。将 B
注册为 lambda,它可以使用参数。
builder.RegisterType<BFactory>().SingleInstance();
builder.Register((c, p) =>
{
var appId = p.Named<string>("appId");
var factory = c.Resolve<BFactory>();
return factory.GetB(appId);
});
现在你可以解析一个B
只要有一个参数传递给Resolve
,比如
using var scope = container.BeginLifetimeScope();
var b = scope.Resolve<B>(new NamedParameter("appId", "my-app-id"));
您可以为 A
构建类似的东西,但是 AFactory
可以将 BFactory
作为参数,因此它可以获得 A
的正确实例。
public class AFactory
{
private ConcurrentDictionary<string, B> _cache = new ConcurrentDictionary<string, B>();
private readonly BFactory _factory;
public AFactory(BFactory factory)
{
this._factory = factory;
}
public A GetA(string appId)
{
return this._cache.GetOrAdd(
appId,
a => new A(a, this._factory.GetB(a)));
}
}
同样的事情,将工厂注册为单例并从工厂获取A
。
builder.RegisterType<AFactory>().SingleInstance();
builder.Register((c, p) =>
{
var appId = p.Named<string>("appId");
var factory = c.Resolve<AFactory>();
return factory.GetA(appId);
});
你也可以喜欢它,比如如果有一些东西需要来自容器,就可以使用 Func relationships。例如,假设您的 B
class 真的看起来像这样:
public class B
{
public B(string appId, IComponent fromContainer) { /* ... */ }
}
在那里,也许 IComponent
需要来自容器,但 appId
来自参数。你可以让 BFactory
变成这样:
public class BFactory
{
private ConcurrentDictionary<string, B> _cache = new ConcurrentDictionary<string, B>();
private ILifetimeScope _scope;
public BFactory(ILifetimeScope scope)
{
// This will be the CONTAINER / root scope if BFactory
// is registered as a singleton!
this._scope = scope;
}
public B GetB(string appId)
{
return this._cache.GetOrAdd(
appId,
a => {
// Auto-generated factory! It will get IComponent from
// the container but let you put the string in as a parameter.
var func = this._scope.Resolve<Func<string, B>>();
return func(a);
});
}
}
请注意,如果您使用 auto-generated 工厂事物(Func<string, B>
事物),您将需要自己注册 B
,例如:
// You can't register the factory as the provider for B
// because it's cyclical - the BFactory will want to resolve
// a Func<string, B> which, in turn, will want to execute the
// BFactory.
builder.RegisterType<B>();
builder.RegisterType<BFactory>().SingleInstance();
这意味着您必须转换代码以使用 BFactory
而不是 B
。您可能可以随意使用它来使其工作,但您明白了 - 您将不得不自己创建缓存机制,这就是您将挂钩到 Autofac 中的内容。希望上面的片段可以给你一些想法,你可以扩展并让你畅通无阻。
我需要创建一个实例,不是基于类型或生命周期范围声明,而是基于构造函数的字符串参数的值。对于另一个 class 的依赖实例,我也需要同样的效果,在其自己的无关构造器中使用相同的字符串。
class A {
public A(string appId, B b) {}
}
class B {
public B(string appId) {}
}
在上面的示例中,我需要为 appId 的唯一值创建一个 A 单例和一个 B 单例。
我可以使用 TypedParameter 解析 A 和 B,但是我无法弄清楚每个 appId 值的单例部分。我只尝试了 A 来简化(不涉及依赖 B)。我查看了文档中的 Keyed、Indexed 等,但 none 似乎适合每个用户定义的键值单例,除非我编写自己的 Register lambda,它使用我自己的唯一 appId 内存缓存键。
Autofac 是否有内置的简洁方式来强制执行此操作?
这个问题不会有很好的解决方案,因为依赖注入通常是基于类型,而不是参数值。这与基于参数值为 Web 应用程序设置缓存不同。更具体地说,Autofac 没有任何可以帮助您的“内置”内容。 (据我所知,任何其他 DI 框架也没有。)
您可能需要做的是创建一个小型工厂来为您进行缓存并使用它。
这是一个例子,我没有编译也没有测试,但我突然想到让你畅通无阻。
首先,您需要为您的 B
class 创建一个小工厂。
public class BFactory
{
private ConcurrentDictionary<string, B> _cache = new ConcurrentDictionary<string, B>();
public B GetB(string appId)
{
return this._cache.GetOrAdd(
appId,
a => new B(a));
}
}
现在您将 BFactory
注册为单例,这将使您获得想要的 one-instance-per-app-ID 行为。将 B
注册为 lambda,它可以使用参数。
builder.RegisterType<BFactory>().SingleInstance();
builder.Register((c, p) =>
{
var appId = p.Named<string>("appId");
var factory = c.Resolve<BFactory>();
return factory.GetB(appId);
});
现在你可以解析一个B
只要有一个参数传递给Resolve
,比如
using var scope = container.BeginLifetimeScope();
var b = scope.Resolve<B>(new NamedParameter("appId", "my-app-id"));
您可以为 A
构建类似的东西,但是 AFactory
可以将 BFactory
作为参数,因此它可以获得 A
的正确实例。
public class AFactory
{
private ConcurrentDictionary<string, B> _cache = new ConcurrentDictionary<string, B>();
private readonly BFactory _factory;
public AFactory(BFactory factory)
{
this._factory = factory;
}
public A GetA(string appId)
{
return this._cache.GetOrAdd(
appId,
a => new A(a, this._factory.GetB(a)));
}
}
同样的事情,将工厂注册为单例并从工厂获取A
。
builder.RegisterType<AFactory>().SingleInstance();
builder.Register((c, p) =>
{
var appId = p.Named<string>("appId");
var factory = c.Resolve<AFactory>();
return factory.GetA(appId);
});
你也可以喜欢它,比如如果有一些东西需要来自容器,就可以使用 Func relationships。例如,假设您的 B
class 真的看起来像这样:
public class B
{
public B(string appId, IComponent fromContainer) { /* ... */ }
}
在那里,也许 IComponent
需要来自容器,但 appId
来自参数。你可以让 BFactory
变成这样:
public class BFactory
{
private ConcurrentDictionary<string, B> _cache = new ConcurrentDictionary<string, B>();
private ILifetimeScope _scope;
public BFactory(ILifetimeScope scope)
{
// This will be the CONTAINER / root scope if BFactory
// is registered as a singleton!
this._scope = scope;
}
public B GetB(string appId)
{
return this._cache.GetOrAdd(
appId,
a => {
// Auto-generated factory! It will get IComponent from
// the container but let you put the string in as a parameter.
var func = this._scope.Resolve<Func<string, B>>();
return func(a);
});
}
}
请注意,如果您使用 auto-generated 工厂事物(Func<string, B>
事物),您将需要自己注册 B
,例如:
// You can't register the factory as the provider for B
// because it's cyclical - the BFactory will want to resolve
// a Func<string, B> which, in turn, will want to execute the
// BFactory.
builder.RegisterType<B>();
builder.RegisterType<BFactory>().SingleInstance();
这意味着您必须转换代码以使用 BFactory
而不是 B
。您可能可以随意使用它来使其工作,但您明白了 - 您将不得不自己创建缓存机制,这就是您将挂钩到 Autofac 中的内容。希望上面的片段可以给你一些想法,你可以扩展并让你畅通无阻。