The*_*hud 7 c# dependency-injection inversion-of-control light-inject
我最近和IOC Containers(在我的案例中是LightInject)有点讨厌.
我一直在读你应该只需要在启动时使用容器ONCE,而不是在其他地方.这是我难以理解的.如果我只能在引导程序/启动方法中引用容器,那么如何在课程依赖于用户输入的情况下,如何在项目中或在运行时解析我需要的内容.
所以在我的传统Windows窗体应用程序中,在Form Load Say上,我会根据下面的代码启动Lightinject.这只是一个随意的例子,它更像是我需要解决的前提.
我可能完全错过了这里的东西,或者只是没有得到它.但是我应该如何解决依赖性,如果我不能使用/不应该引用或使用Container.GetInstance/Resolve/{选择IOC语法Here},并且仅在组合根目录中.
对于Instance,我说我的表单上有两个按钮和一个TextBox.第一个按钮给我一个ILoader(下面的代码),第二个按钮加载一个文件查看器(ILoader,代码下面),其文件名是输入winform上文本框的内容.
没有IOC容器我会做以下(让我们假设它放在click事件中)
按钮1单击事件:
ISplitText MyStringFunc = new WhateverImplementsIt();
Run Code Online (Sandbox Code Playgroud)
按钮2(根据文本框输入获取文件阅读器)
ILoader MyLoader = new FileReaderImplementation(TextBox1.Text);
Run Code Online (Sandbox Code Playgroud)
使用LightInject,我肯定不得不做以下事情:
Button1点击:
ISplitText Splitter = Container.GetInstance<ISplitText>();
Run Code Online (Sandbox Code Playgroud)
按钮2单击
var LoaderFunc = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = LoaderFunc(TextBox1.Text);
Run Code Online (Sandbox Code Playgroud)
我不正确吗?在一个大型项目中,我将拥有Container.GetInstance,遍布整个地方,在主窗体文件和其他地方,所以我怎么能只在引导程序的形式中仅在1个位置引用容器,我错过了一个魔法一块拼图?
在所有示例应用程序中,我看到它都是在一个简单的控制台应用程序中,在Main函数中完成的.所有这些应用程序遵循以下格式:
Container = new Container();
Container.Register<IFoo,Foo>();
Container.Register<IBar,Bar();
var Resolved = Container.GetInstance<IFoo>();
Run Code Online (Sandbox Code Playgroud)
嗯,我理解这一切,而且非常简单.一旦你开始为应用程序本身添加一些复杂性,我就失去了如何获取实例而不使Container本身公开,静态,或以某种方式,形状或形式访问然后调用Container.GetInstance在一百万个地方(显然,这是一个很大的否定).请帮忙!干杯,
CHUD
PS - 我并不担心"抽象容器"本身.所以我宁愿只专注于增加我对上述的理解.
public class BootStrapIOC
{
public ServiceContainer Container;
public BootStrapIOC(ServiceContainer container)
{
Container = container;
}
public void Start()
{
Container.Register<ISplitText, StringUtil>();
Container.Register<string, ILoader>((factory, value) => new FileViewByFileName(value));
}
}
//HUH? How can i NOT use the container??, in this case in the button_click
ILoader Loader = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = Loader(TextBox1.Text);
ISplitText Splitter = Container.GetInstance<ISplitText>();
Run Code Online (Sandbox Code Playgroud)
编辑#1
好的,所以,在重新阅读评论并在互联网上查看更多示例之后,我想我可能终于明白了.问题是(我认为)是我没有想到"更高级别".我试图在我的winforms应用程序中解决我的依赖性,在表单已经构建之后,以及表单本身.实际上,到那时为时已晚.我没有将"表单本身"视为另一个对象,它需要将其注入其中的依赖项.
所以我现在在我的Program.cs中引导:
static class Program
{
private static ServiceContainer Container;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Container = new ServiceContainer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
BootStrapIOC Strap = new BootStrapIOC(Container);
Strap.Start();
//This magic line resolves EVERYTHING for me required by the Form
var form = Container.GetInstance<Form1>();
Application.Run(form);
//Application.Run(new Form1());
}
}
Run Code Online (Sandbox Code Playgroud)
我现在的问题是,我的思路现在在winforms方面是否正确.它似乎更有意义,改变我的方法"更高"链和从Program.cs解决?
其次,我不确定这是否需要一个新的问题,请告诉我,因为我是一个SO菜鸟.
如何设置工厂以返回正确的对象实例?其中一条原始评论表明,这将是这种情况下的一种用法.让我们用一个人为的例子.我需要一个对象,但在运行时/用户输入之前不知道哪个对象.
我的想法:
BootStrap Container.Register();
工厂接口和实现:我们还要提供一些可选参数,因为我想知道这是否是正确/最好的方法呢?
public interface IFileViewerFactory
{
ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false);
}
public class FileViewerFactory:IFileViewerFactory
{
public FileViewerFactory() { }
public ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false)
{
if (CreatingDirectory == false)
{
if (Directory == null)
return new FileViewByFileName(FileName);
else
return new FileViewByDirectoryName(Directory, FileName);
}
else
return new FileViewByDirectoryNameCreateDirectoryOptional(Directory, FileName, CreatingDirectory);
}
}
Run Code Online (Sandbox Code Playgroud)
形成:
public IFileViewerFactory FileViewerFactory { get; set; }
Run Code Online (Sandbox Code Playgroud)
按钮单击:
ILoader FileLoader = FileViewerFactory.GetFileViewer(TxtBoxFileName.Text);
Run Code Online (Sandbox Code Playgroud)
要么:
ILoader FileLoader = FileViewerFacotry.GetFileViewer(TxtBoxFileName.Text,TxtBoxDirectory.Text);
Run Code Online (Sandbox Code Playgroud)
所以最后,我的问题是:
我知道现在回答这个已经一年多的问题有点晚了,但让我尝试一下。
这里的问题是,您不希望您的容器超出您的组合根之外的任何其他位置。在由多个组合组成的复杂解决方案中,这意味着容器本身仅由最顶层的组合(组合根所在的位置)引用。
但应用程序堆栈通常很复杂,您可能有多个组合,但您的依赖关系应该在整个应用程序中解决。
从历史上看,可能的方法之一是服务定位器模式。定位器深入到堆栈的最底部,并从那里提供解决依赖关系的服务。因此,它可以在堆栈的任何位置使用。
这种方法有两个缺点,首先是您的容器在堆栈的最底部被引用,即使您规避了这一点,您的定位器仍然在各处被引用。后者在大型应用程序中可能会很痛苦,因为您可能有一些独立的组合,您不希望被迫引用您的定位器(或其他任何东西)。
最终的解决方案称为本地工厂(又名依赖解析器),它负责创建少数依赖服务。诀窍是在您的应用程序中拥有多个本地工厂。
典型的设置是这样的。假设有一个程序集(称为 )A,客户端将使用它来获取 的实例IServiceA。该程序集仅包含两个:
IServiceA仅此而已,没有其他引用,没有容器。目前甚至还没有实施。这里的技巧是让工厂对实际的提供者开放 - 从某种意义上说,工厂甚至还不知道如何创建实例 - 由组合根来告诉它。
// Assembly A
public interface IServiceA
{
...
}
public class ServiceAFactory
{
private static Func<IServiceA> _provider;
public static void SetProvider( Func<IServiceA> provider )
{
_provider = provider;
}
public IServiceA Create()
{
return _provider();
}
}
Run Code Online (Sandbox Code Playgroud)
这里的提供者有一个功能契约,但它也可以表示为一个接口
就这样,虽然目前工厂还没有实现,但客户端代码突然变得非常稳定:
// client code to obtain IServiceA
var serviceA = new ServiceAFactory().Create();
Run Code Online (Sandbox Code Playgroud)
再次注意这个程序集是如何独立的A。它没有其他引用,但它提供了一种获取服务实例的干净方法。其他程序集可以引用此程序集,而无需其他附加引用。
然后是组合根。
在堆栈的最顶部,主程序集引用该程序集A和一些其他程序集,我们称其AImpl为包含服务接口的可能实现。
从技术上讲,服务的实现可以与接口位于同一个程序集中,但这只会让事情变得更容易
组合根通过将工厂方法委托给堆栈来创建工厂的提供者A
// Composition Root in the top level module
// Both assemblies
// * A that contains IServiceA
// * AImpl that contains an implementation, ServiceAImpl
// are referenced here
public void CompositionRoot()
{
ServiceAFactory.SetProvider( () =>
{
return new ServiceAImpl();
} );
}
Run Code Online (Sandbox Code Playgroud)
从现在开始,提供者已设置完毕,并且使用工厂的堆栈中的所有客户端代码都可以成功获取实例。
组合根还提供其他本地工厂的所有其他实现。组合根中有多个设置:
SomeLocalFactoryFromAssemblyA.SetProvider( ... );
AnotherLocalFactoryFromAssemblyB.SetProvider( .... );
...
Run Code Online (Sandbox Code Playgroud)
那么你的容器在哪里?
嗯,容器只是提供者的一种可能的实现。它只会有帮助,不会破坏。但请注意,您甚至不必使用它,这是一种选择而不是义务。
public void CompositionRoot()
{
var container = new MyFavouriteContainer();
container.Register<IServiceA, ServiceAImpl>(); // create my registrations
ServiceAFactory.SetProvider( () =>
{
// this advanced provider uses the container
// this means the implementation, the ServiceAImpl,
// can have possible further dependencies that will be
// resolved by the container
container.Resolve<IServiceA>();
} );
}
Run Code Online (Sandbox Code Playgroud)
这是我所知道的最干净的设置。它具有所有所需的功能:
| 归档时间: |
|
| 查看次数: |
638 次 |
| 最近记录: |