有没有像 Unity 这样的东西可以完成不需要界面的简单事情?
也许我误用了 Unity,但事情就是这样。我有几个应用程序,它们都加载相同的插件程序集。所有程序集都需要一个库,我希望它们能够通过 Unity 访问该库。但是,为了使用 Unity 或任何其他 IoC 框架,我必须为该库编写一个接口。我可能会这样做,但由于除了支持 Unity 之外,实际上并不需要界面,因此我担心这意味着我 1) 没有抓住要点,或者 2) 误用了框架。如果我避免为我提供 DI 的东西,那么我必须将库类设置为单例,然后将其传递给所有插件构造函数,或通过公共属性传递,但我不想这样做。
也就是说,在尚未使用 Unity 实际实现任何内容的情况下,我没有得到其他细节 - 尽管 Unity 会让我通过 Resolve<> 请求库,但我的插件仍然需要引用 Unity 实例在主应用程序中创建。那么在这种情况下,您唯一的选择是将 Unity 引用传递给所有插件,但从那时起就很方便,仅仅因为您可以使用 Unity 来获取所有其他依赖项?
更新
我意识到我错过了重点,但希望有人能为我澄清——我不应该到处传递对 Unity 的引用!我只需要在我的应用程序中创建容器,然后注册所有类型。然后,当我实例化所有插件时,它们应该神奇地能够使用这些注册的接口,几乎不需要额外的努力,对吗?就我而言,我的构造函数必须是无参数的,因为我的插件加载器无法处理参数,在这种情况下,我必须使用属性注入来让它们访问接口,对吧?
另一个更新
我继续尝试了 Unity。我注册了所有插件所需的类的实例。我还知道我的插件加载器最终会遇到问题,因为它们是无参数的(并且我可能需要将其传递给 Unity 才能使它们工作)。不过,目前我正在直接创建一个插件,并且是通过 Resolve 方法来完成的。所以代码基本上是这样的:
// app code
ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>( _candy_settings);
CandyPlugin _plugin = unity.Resolve<Candy>(); // throws null reference exception, see below.
// plugin code
public class Candy
{
[Dependency]
ICandySettings CandySettings { get; set; }
...
public Candy()
{
CandySettings.GetSetting("box"); // CandySettings is null! why? Didn't Unity do this for me?
}
}
所以我现在的问题是,我希望(鉴于我有限的知识),Unity 将自动将插件的 CandySettings 引用设置为通过 RegisterInstance 注册的任何实例,但事实并非如此。
一个可行的选项
如果我跳过烟雾和镜子的东西,只是将我的 UnityContainer 传递到插件的构造函数中,那么我可以调用 Unity.Resolve() 来设置我的 CandySettings 属性的值,一切都很好。我很想知道为什么 [Dependency] 属性没有按照我的预期行事。如果我没记错的话,我实际上不需要将 Unity 传递给插件加载器中的每个构造函数。我应该只使用 Unity.Resolve() ,如果 [Dependency] 正常工作,它可能会工作。然而,现在我明白了每个人都在说如何选择 IoC 容器然后将其强加给整个开发团队。
MEF!
到目前为止,MEF 正在为我赢得这场战斗。这非常简单,神奇的烟雾和镜子的东西非常适合我的需求(目前)。但我仍然想让 Unity 正常工作。我觉得奇怪的是,对于 MEF,我只需要组合各个部分,其他所有内容都就位,而我似乎无法让 Unity 自动注入东西,我必须通过对 Unity 的引用来解决所有问题到处。这不可能是正确的。
更多 MEF
我喜欢这样的想法,即可以非常轻松地使用 MEF 解析多个对象,但是如果我使用策略模式来指示代码行为,情况又如何呢?目前,它就像将引用从一种行为的实现更改为另一种行为一样简单,并且它就可以工作。有人用 MEF 这样做吗?使用 ImportMany,然后使用额外的代码来确定应该调用列表中的哪些行为是正确的方法吗?
Perhaps I'm misapplying Unity, but here goes. I have a couple of applications, both of which load the same plugin assemblies. All assemblies require a library, and I want them to be able to access this library via Unity. However, in order to use Unity, or any other IoC framework, I'd have to write an interface for this library. I will probably do this, but since an interface isn't really needed for anything other than to support Unity, I am afraid that this means that I am 1) missing the point, or 2) misapplying the framework. If I avoid something that offers me DI, then I'd have to make the library class a singleton, and then pass it to all of the plugin constructors, or via a public property, and I don't want to do this.
That said, and without actually implementing anything with Unity yet, I'm not getting one other detail -- although Unity will let me request the library via Resolve<>, my plugins will still need to have a reference to the Unity instance that is created in the main applications. So is this a case where your only option is to pass the Unity reference to all of the plugins, but then it's convenient from that point on, merely because you can use Unity to get at all of the other dependencies?
UPDATE
I realized that I missed the point, but hopefully someone can clarify for me -- I shouldn't be passing a ref to Unity everywhere! I should only have to create the container in my app, and then register all of the types afterward. Then when I instantiate all of my plugins, they should just magically be able to use those registered interfaces, with almost no extra effort, right? In my case, my constructors must be parameterless because my plugin loader can't deal with arguments, and in that case, I'll have to use property injection to give them access to the interfaces, right?
ANOTHER UPDATE
I went ahead and tried out Unity. I registered the instance of my class that's needed by all plugins. I also know that I would eventually run into a problem with my plugin loader since they are parameterless (and I might need to pass it a ref to Unity to make them work). However, just for now I am directly creating a plugin, and I am doing that via the Resolve method. So here's basically what the code looks like:
// app code
ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>( _candy_settings);
CandyPlugin _plugin = unity.Resolve<Candy>(); // throws null reference exception, see below.
// plugin code
public class Candy
{
[Dependency]
ICandySettings CandySettings { get; set; }
...
public Candy()
{
CandySettings.GetSetting("box"); // CandySettings is null! why? Didn't Unity do this for me?
}
}
So my problem right now is that I would expect (given my limited knowledge), that Unity is going to automagically set the plugin's CandySettings reference to whatever instance was registered via RegisterInstance, but it's not.
A WORKING OPTION
If I skip the smoke and mirrors stuff and just pass my UnityContainer into the plugin's constructor, then I can call Unity.Resolve() to set the value of my CandySettings property, and everything works great. I'd love to know why the [Dependency] attribute isn't doing what I thought it would. If I'm not mistaken, I actually shouldn't need to pass Unity to each constructor in my plugin loader. I should just use Unity.Resolve() and it would presumably work if [Dependency] is working. However, now I understand what everyone is saying about how picking an IoC container will then force it on your entire development team.
MEF!
So far, MEF is winning the battle for me. It's pretty simple, and the magical smoke and mirrors stuff works great for my needs (currently). But I'd still like to get Unity working. I find it strange that for MEF, I only need to compose the parts and everything else just falls into place, whereas I can't seem to get Unity to just inject stuff automatically, and I have to Resolve everything through a reference to Unity passed everywhere. This can't be right.
More MEF
I like the idea that I can resolve multiple objects with MEF very easily, but what about cases where I'm using the Strategy pattern to dictate code behavior? Currently, it's as simple as changing the reference from one implementation of a behavior to another, and it just works. Does anyone do this with MEF? Is the right way to do it to use ImportMany, and then use extra code to determine which behavior in the list should get invoked?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您的依赖注入要求很小(从听起来来看,确实如此),您可能需要考虑尝试 MEF 。它是轻量级且易于使用的,并且具有直接位于 .NET 4 框架中的优点,这意味着如果您迁移到 .NET 4,则不需要额外的部署要求。
在此期间,它通过 Codeplex 在 3.5 上得到支持地点。
MEF 在这方面很出色,因为它可以在任何类型上工作,而不仅仅是接口。
话虽这么说,如果您希望插件使用应用程序提供的“库”,那么您很可能始终需要该库可用并由您的插件引用(或者至少需要一组基类或接口) API)。
If your dependency injection requirements are minor (which, from the sounds of it, they are), you might want to consider trying MEF. This is lightweight and easy to use, and has the advantage of being in the framework directly in .NET 4, which means no extra requirements to deploy if you ever move to .NET 4.
In the interim, it's supported on 3.5 via the Codeplex site.
MEF is nice in this since it can work on any type, not just interfaces.
That being said, if you want plugins to use a "library" provided by your application, you're most likely going to always need that library to be available and referenced by your plugins (or at least a set of base classes or interfaces for the API).
大多数 IoC 容器都可以映射具体的类和接口,只是大多数人认为使用接口是更好的实践,因为它与设计和测试有关。
如果您只想允许配置一些常见类型,那么您可能需要使用 System.IServiceProvider 来推出自己的类型。它是内置的,因此您无需将 IoC 依赖项级联到您的使用代码。
或者您可以在 Common Service Locator 库中拥有一个单例服务定位器
编辑:鉴于您的更新,我'建议在实例化插件实例后使用 BuildUp 在插件实例上注入属性依赖项。请参阅本文
Most IoC containers can map concrete classes as well as interfaces, it's just that using interfaces is considered by most to be a better practice as it relates to design and testing.
If all you want to do is allow provisioning of a few common types, you might want to roll your own using System.IServiceProvider. it's built-in so you wouldn't need to cascade your IoC dependencies to your consuming code.
Or you could have a singleton service locator as in the Common Service Locator library
EDIT: Given your updates, i'd suggest using BuildUp to inject property dependencies on your plugin instances after you've instantiated them. See this article
首先,我有一个单例,它保存对我的 Unity 容器实例的引用,因此我不必将它传递到各处。
其次,您可以自由使用
unityContainer.RegisterInstance(myInstance);
或unityContainer.RegisterType();
First, I have a singleton which holds a reference to my Unity container instance, so I don't have to pass it around everywhere.
Second, you are free to use
unityContainer.RegisterInstance(myInstance);
orunityContainer.RegisterType<MyClass, MyClass>();
Unity 在发布的示例中无法正常工作的原因是
默认可访问性是私有的。 Unity 要求该属性是公共的(因此它可以调用 setter)。
在最新版本的 Unity 中(错误消息/行为可能与旧版本不同),使用您发布的代码时出现异常:“ConsoleApplication32.Candy 类型上的属性 CandySettings 不可设置。”
是和不是。显然,需要连接类,以便正确注入依赖项(希望在 Composition Root 中)。但是,应用程序代码不会(或不应)引用容器(例如使用 DependencyAttribute 或传入 IUnityContainer)。在发布的示例中,这可以通过为
Candy
类注册InjectionProperty
来完成:我(以及许多其他人)也更喜欢在构造函数中注入依赖项(构造函数注入)在这种情况下,Unity 将做正确的事情并将依赖项注入到构造函数中:
注册代码将与原始问题中的代码相同:
The reason why Unity was not working properly in the posted example is that in
the default accessibility is private. Unity requires the property to be public (so it can call the setter).
In the latest version of Unity (the error message/behavior might have been different with an older version) using your posted code I get an exception: "The property CandySettings on type ConsoleApplication32.Candy is not settable."
Yes and no. Obviously classes will need to be wired up so that dependencies are properly injected (hopefully in the Composition Root ). However, the application code does not (or should not) reference the container (e.g. using DependencyAttribute or passing in IUnityContainer). In the posted example this can be done by registering an
InjectionProperty
for theCandy
class:I (along with many others) also prefer injecting the dependencies in the Constructor (Constructor Injection) in which case Unity will just do the right thing and inject the dependency in the constructor:
The registration code would just be the same as in the original question: