警告,前方长篇大论。
我最近一直在思考这个问题,我正在努力在这里找到一个令人满意的解决方案。我将使用 C# 和 autofac 作为示例。
问题
IoC 非常适合构建大型无状态服务树。我解析服务并将数据仅传递给方法调用。伟大的。
有时,我想将数据参数传递给服务的构造函数。这就是工厂的用途。我没有解析服务,而是解析了它的工厂并使用参数调用 create 方法来获取我的服务。更多的工作,但还可以。
有时,我希望我的服务在一定范围内解析到同一个实例。Autofac 提供了InstancePerLifeTimeScope()
非常方便的功能。它允许我始终解析到执行子树中的同一个实例。好的。
有时我想将这两种方法结合起来。我想要构造函数中的数据参数并有实例范围。我还没有找到令人满意的方法来实现这一点。
解决方案
1.初始化方法
无需将数据传递给构造函数,只需将其传递给Initialize
方法。
界面:
interface IMyService
{
void Initialize(Data data);
void DoStuff();
}
班级:
class MyService : IMyService
{
private Data mData;
public void Initialize(Data data)
{
mData = data;
}
public void DoStuff()
{
//...
}
}
登记:
builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();
用法:
var myService = context.Resolve<IMyService>();
myService.Init(data);
// somewhere else
var myService = context.Resolve<IMyService>();
在第一次解析服务并调用 Initialize 后,我可以愉快地在相同的上下文中解析并获得相同的初始化实例。我不喜欢这样一个事实,即在调用之前Initialize
我有一个无法使用的对象。在我调用 Initialize() 之前,存在实例将被解析并在其他地方使用的危险。
2.持有人模式
这是一种保存对数据对象的引用的模式,而不是注入数据对象本身,而是注入持有者对象。
界面:
interface IMyService
{
void DoStuff();
}
班级:
class MyService : IMyService
{
private Data mData;
public MyService(IDataHolder dataHolder)
{
mData = dataHolder.Data;
}
public void DoStuff()
{
//...
}
}
登记:
builder.RegisterType<MyService>().As<IMyService>();
builder.RegisterType<DataHolder>().As<IDataHolder>().InstancePerLifetimeScope();
用法:
var holder = context.Resolve<IDataHolder>();
holder.Data = data;
// somewhere else
var myService = context.Resolve<IMyService>();
当我将持有实例的责任转移到不同的类时,这会好一些。我现在也可以在其他服务中使用该持有人。另一个优点是我可以在必要时热交换持有人中的数据。我不喜欢它混淆代码并添加另一个我必须在测试期间模拟的接口的事实。
3.让容器持有实例
界面:
interface IMyService
{
void DoStuff();
}
班级:
class MyService : IMyService
{
private Data mData;
public MyService(Data data)
{
mData = dataHolder.Data;
}
public void DoStuff()
{
//...
}
}
登记:
builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();
用法:
var myServiceFactory = context.Resolve<Func<Data, IMyService>>();
myServiceFactory(data);
// somewhere else
var myService = context.Resolve<IMyService>();
这是正确的。我不会在任何地方存储工厂调用的结果,因为 autofac 会为我存储它。这对于任何会阅读代码的人来说都是非常令人惊讶的。我不确定 autofac 是否甚至打算像这样使用。这样做的好处是我既不需要额外的初始化方法,也不需要额外的类来保存实例。
问题
你对此有什么看法?您如何处理运行时数据参数和生命周期范围的情况?我错过了更好的方法吗?