到目前为止,我一直在使用ASP.NET MVC学习使用Castle.Windsor的IoC / DI,但我有一个在Windows Forms中完成的副项目,我想知道是否有一种有效的方法用它来做。
我的问题在于表单,服务等的创建。在ASP.NET MVC中,有一种“激活器”可以做到这一点,但在Windows窗体中并非如此。我必须创建一个像var form = new fclsMain();
这样的新表单,这样的表格就像..
class fclsMain : System.Windows.Forms.Form
{
private readonly ISomeRepository<SomeClass> someRepository;
fclsMain(ISomeRepository<SomeClass> someRepository)
{
this.someRepository = someRepository;
}
}
有点短暂。我基本上要做...
var form = new fclsMain(IoC.Resolve<ISomeRepository<SomeClass>);
正如我在至少三个问题中指出的那样,并不聪明,因为据说这不是IoC的“正确”用法。
那么我如何使用Castle.Windsor和Windows Forms?有没有办法设计Form Activator或其他东西?我真的迷路了,如果我不能制作一个我可以解决的静态IoC
容器,我该怎么办?
答案 0 :(得分:1)
在这里你做的事情不是很“依赖注入”......
var form = new fclsMain(IoC.Resolve<ISomeRepository<SomeClass>);
“新”是问题...... 你必须致电
var form = IoC.Resolve<fcls>();
必须通过Fluent Registration API o
正确配置fcls类型的形式答案 1 :(得分:0)
要在整个应用程序中使用相同的Castle容器,请创建一个静态类,如:
public static class CastleContainer {
private static IWindsorContainer container;
public static IWindsorContainer Instance {
get {
if (container == null) {
container = new WindsorContainer();
}
return container;
}
// exposing a setter alleviates some common component testing problems
set { container = value; }
}
// shortcut to make your life easier :)
public static T Resolve<T>() {
return Instance.Resolve<T>();
}
public static void Dispose() {
if (container != null)
container.Dispose();
container = null;
}
}
然后在Main()
方法中注册/安装所有组件。您还可以挂钩应用程序关闭事件以调用Dispose()
(尽管这不是关键)。
Castle实际上在quick-start guide中使用了Windows窗体应用。
我上面展示的模式是服务定位器的变体,有些人称之为反模式。它的声誉很差,因为除了其他原因之外,它还引用了Windsor的代码库。理想情况下,您只需拨打一次container.Resolve<...>()
即可创建根表单。所有其他服务&amp;形式通过构造函数注入。
实际上,您可能需要再调用一次Resolve,特别是如果您不想在启动时加载应用程序的每个角落。在Web世界中,最佳实践是将容器交给Web框架。在Windows窗体世界中,您需要实现自己的服务定位器,如上所述。 (是的,将容器交给ASP.NET MVC框架仍然是服务定位器模式)。
我编辑了上面的代码示例,以便静态容器可以注入;没有资源在静态上下文中被束缚。如果您最终创建自己的服务定位器,您可能还想创建一个像这样的测试实用程序,以使测试更容易。
public static class TestUtilities
{
public static IContainer CreateContainer(Action<IContainer> extraConfig = null)
{
var container = new WindsorContainer();
// 1. Setup common mocks to override prod configuration
// 2. Setup specific mocks, when provided
if (extraConfig != null)
extraConfig(container);
// 3. Configure container with production installers
CastleContainer.Instance = container;
return container;
}
}
这使得创建一个看起来很像生产版本的新容器的快捷方式,但是有些服务被模拟替换了。一些示例测试可能如下所示:
[Test]
public void SubComponentWorksGreat()
{
using (var container = TestUtilities.CreateContainer())
{
var subComponent = container.Resolve<SubComponent>();
// test it...
}
}
[Test]
public void SubComponentWorksGreatWithMocks()
{
var repoMock = new Mock<IRepository>();
using (var container = TestUtilities.CreateContainer(c =>
c.Register(Component.For<IRepository>().Instance(repoMock.Object))))
{
var subComponent = container.Resolve<SubComponent>();
// test it with all IRepository instances mocked...
}
}
最后一点。为每个测试创建一个完整的容器可能会变得昂贵。另一种选择是创建完整容器,但仅使用嵌套容器进行实际测试。
答案 2 :(得分:0)
正如你所说,你没有“必须”新建一张表格。 我使用WinForms并且从不调用“new FormName()”。它本身就是一种依赖。否则,我必须将构造函数填满服务定位器调用。
我可能只在最顶层使用ServiceLocator(如另一个答案) BUT 。 例如,我实现了一个Command模式来拦截工具栏按钮。 看起来像这样:
public void Handle(string commandName)
{
var command = IoC.Resolve<ICommand>(RegisteredCommands[commandName]);
command.Execute();
}
然后,在简化的情况下,这是在其他地方编写的代码:
public class ShowOptionsCommand : Command, ICommand
{
private readonly IOptionsView _optionsView;
public ShowOptionsCommand(IOptionsView optionsView)
{
_optionsView = optionsView;
}
public void Execute()
{
_optionsView.Show();
}
}
是的,我使用的是“服务定位器”,但你几乎看不到它。 这对我来说很重要,因为在整个代码中都有服务定位器调用(例如在每个类中)都会失去使用控制依赖性反转的一些点。需要额外的工作才能测试等