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();
IUn开发者_运维技巧ityContainer 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?
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).
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.
public class SomePlugin
{
public SomePlugin(IServiceProvider serviceProvider)
{
_foo = serviceProvider.GetService(typeof(IFoo)) as IFoo;
}
}
Or you could have a singleton service locator as in the Common Service Locator library
public class SomePlugin
{
public SomePlugin()
{
_foo = ServiceLocator.Current.GetService(typeof(IFoo)) as IFoo;
}
}
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
PluginInstance plugin = // already loaded from whatever you're already doing
container.BuildUp<PluginInstance>(plugin);
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);
or unityContainer.RegisterType<MyClass, MyClass>();
The reason why Unity was not working properly in the posted example is that in
[Dependency]
ICandySettings CandySettings { get; set; }
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."
However, now I understand what everyone is saying about how picking an IoC container will then force it on your entire development team.
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 the Candy
class:
ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>( _candy_settings);
unity.RegisterType<Candy>(new InjectionProperty("CandySettings"));
CandyPlugin _plugin = unity.Resolve<Candy>();
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:
public class Candy
{
public ICandySettings CandySettings { get; private set; }
...
public Candy(ICandySettings candySettings)
{
this.CandySettings = candySettings;
this.CandySettings.GetSetting("box");
}
}
The registration code would just be the same as in the original question:
ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>(_candy_settings);
var _plugin = unity.Resolve<Candy>(); // Everything is OK :)
精彩评论