【发布时间】:2012-03-09 04:37:34
【问题描述】:
警告,前面的帖子很长。
我最近一直在思考这个问题,我正在努力在这里找到一个令人满意的解决方案。我将使用 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 是否甚至打算像这样使用。这样做的好处是我既不需要额外的初始化方法,也不需要额外的类来保存实例。
问题
您对此有何看法?您如何处理运行时数据参数和生命周期范围的情况?我错过了更好的方法吗?
【问题讨论】:
-
这是一个绝妙的问题,正是我需要找出的问题。太可惜了,它没有得到这么少的关注。
-
我加了一个赏金。让我们看看3年后是否有帮助。这个问题是在我广泛使用 Autofac 作用域的 WPF 应用程序的上下文中。在 MVC/WebAPI 中,这个问题不会出现,因为事物的本质往往更加无状态。
-
据我在 Autofac 文档中看到的。 InstancePerLifetimeScope 需要一个 BeginLifetimeScope() 调用才能工作。你的例子不完整吗?请提供至少 2 个作用域的完整代码块,用于了解您如何使用 autofact 来查看您是否遗漏了什么。
-
好问题!您是否考虑过将服务和数据完全拆分?我的意思是从服务中删除数据(并使其完全无状态)并在服务中的每个操作上传递相关数据?
标签: c# dependency-injection inversion-of-control autofac object-lifetime