Cannot register types whose assemblies are not previously loaded. Is this intended behavior?

Mar 18, 2013 at 3:54 PM
I'm trying to 'convert' a plug-in system of sorts to use the unity container.

The problem I'm having is that I can't register the types from assemblies that were not yet loaded. Since it is a plugin system after all, this makes a lot of sense. I don't want to load the assembly myself at all, but I'd like to register a interface with several types from this plug-in assembly.

I first tried to register the type by using the Type.GetType method passing the fully qualified type name, but it also seems to need the assembly to be pre loaded first.

I then thought about configuring the container through XML, but I get this message:
The type name or alias <type> could not be resolved. Please check your configuration file and verify this type name.

In this case, if I go to the code and add this right before calling LoadConfiguration():
var x = typeof(<type_from_plugin_assembly>);
It works, because the assembly is then loaded to the current domain.

Why does this happen? Is it intended that only loaded assemblies can have registrations? If so, how can I circumvent this in my case? I can't just call something random from the assemblies, since they could be ANY assembly, not just our companies ones.

What we currently have is a NameValueCollection (in a config file) containing a fully qualified type string and a number that will be later used to retrieve the instance. Ideally, it would be just a matter of iterating this config file and registering the types with the plugin interface, using the number as the registration name. I thought this would work, but it does not for the above reasons.

I saw this question in SO, but since it's quite old I decided to ask here too:
http://stackoverflow.com/questions/10733097/preload-objects-using-unity-config

Thanks in advance as always,
Juliano
Mar 18, 2013 at 4:26 PM
As far as I know, Unity does not explicitly handle assembly loading -- that is left to the standard .NET runtime resolution process.

In the case of a plugin architecture where the plugins are in a non-standard location (e.g. ./plugins/) then those assemblies would need to be manually loaded.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Mar 18, 2013 at 5:07 PM
Oh... this is kind of sad. I was really hoping for a solution that worked with what we have now. Unfortunately, these assemblies are in the bin folder, and not on a separate plugins folder. Our current requirement is that our clients could implement our interface and add a line to the config file with a new mapping to their newly created type.

To make this work as it does now (with Activator.CreateInstance) I would have to iterate every assembly and load it manually :(

I'm open to other solutions to the problem here.
Mar 18, 2013 at 7:35 PM
If you know the type you want to register or resolve and the type is in an assembly in the bin folder then there should be no issue:
container.RegisterType<IPlugin, MyAssembly.MyPlugin>();
or just
container.Resolve<MyAssembly.MyPlugin>();
Now usually if you are using a plugin approach you won't know the types ahead of time but the approach you outline should work. Use the config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="pluginSettings" type="PluginCore.PluginConfigurationSection, PluginCore"/>
  </configSections>
  <pluginSettings>
    <add name="MyAssembly.MyPlugin, MyAssembly" value="1"></add>
  </pluginSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>
Then just register all the types specified in the config file:
var config = ConfigurationManager.GetSection(PluginConfigurationSection.PluginSettings) as PluginConfigurationSection;

IUnityContainer container = new UnityContainer();

foreach (var key in config.Settings.AllKeys)
{
    Type type = Type.GetType(key);
    // check if it's an IPlugin if you want a custom error or to ignore
    container.RegisterType(typeof(IPlugin), type , config.Settings[key].Value);
}

var plugin = container.Resolve<IPlugin>("1");
plugin.Do();
As long as the assembly (in my example MyAssembly.dll) is deployed where it can be resolved by the runtime and the type is properly specified then it should work quite well.

The next version of Unity (in development) will have better support for registration by convention which would probably make things a bit easier.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Mar 18, 2013 at 8:16 PM
Thanks a lot for the detailed response.

The approach you describe is exactly what I did when I first tried converting the code (mentioned it in the OP). The Type.GetType() method does not consider assemblies that are not loaded too, thus the solution you describe does not actually work. I even stepped in the code and did a typeof(Plugin).AssemblyQualifiedName in the immediate window and copied that to the config file.

As soon as something related to the assembly is mentioned in the code though (like the code in the OP), the same code starts working and picks up the types correctly.

When I first thought about this problem, I was running tests on a test project. After placing it in our production code however, I realized that it was a nonissue for now, because I was calling BuildManager.GetReferencedAssemblies() to iterate on the available assemblies and register our DALs/BLLs in the web application. Since this method does load every assembly in the bin folder, it works in that environment.

Having said that, I still believe this is user unfriendly. When I first tried the xml registration, I was expecting it to resolve correctly and load the assemblies, since Activator.CreateInstance does this somehow. Don't you guys think that Unity should behave like that? What would be the argument against it? Hasn't anyone else complained here about this specific case scenario (unknown types in external assemblies)?

By the way, I would be looking forward to the autoregistration feature, but I see you will only implement that in the 3.0 branch, and that one requires .net 4.5... we can't use 4.5 yet because we have clients that use Windows Server 2003 or Windows XP. I guess I will have to stick with the third party implementation on nuget (Unity Auto Registration). I've been using this one since last week with mixed results.
http://autoregistration.codeplex.com/

Will you support registration by convention via config files also? That would be quite cool actually.

Thanks again Randy, you've been a huge help with all my questions.
Juliano
Mar 18, 2013 at 11:44 PM
The approach you describe is exactly what I did when I first tried converting the code (mentioned it in the OP)
That's why I did it that way. :)

I'm curious what exception are you getting?

Take a look at this sample: http://sdrv.ms/ZoGGeU.

In that solution there are 3 projects/assemblies:
  1. PluginCore -- contains configuration and IPlugin
  2. MyAssembly -- contains concrete implementation of IPlugin
  3. Console app that reads the config, registers the plugin, resolves the plugin, and invokes the plugin (this is the code I posted above).
The console application does not have a reference to MyAssembly nor does it explicitly use any types from MyAssembly.

Does this work for you?

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Apr 8, 2014 at 11:31 PM
Hi Randy,
Can you please re upload the sample? I'm trying to do a similar project would like to view an example.
Thanks,
Charles
Apr 24, 2014 at 1:47 AM
Sample should still be on OneDrive @ http://sdrv.ms/ZoGGeU : UnityPlugin_437077.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Apr 29, 2015 at 6:31 PM
Short things short, for example, you have a DAL layer that is not directly referenced by the application root (i.e. ASP.NET).
Open the properties windows of the DAL project and under Build set the output path to the bin folder of the application root (ASP.NET Web App).

Now your DAL assembly will be loaded when application starts.
Apr 30, 2015 at 8:38 AM
Unknown (read: unreferenced) types cannot be registered in code. Something like typeof(MyDALClass) won't work if your composition root does not reference that assembly. That is not a limitation of Unity but just the way that .NET works.

If you want to register types from unreferenced assemblies you have to do that via a config file or provide some kind of discoverable registration inside of those unknown assemblies. E.g. some class derived from UnityContainerExtension named "XyzContainerConfiguration" that can be located and added to the container at runtime.

Having said that: Unless your application uses modules that can be dynamically added to your application there is no good reason why your composition root should not reference all assemblies directly.