1

How would one go about resolving Plugin contacts using TinyIoC?

Host.exe /w reference to Core.Contract.dll

var container = new TinyIoCContainer();
container.AutoRegister(new[] { Assembly.LoadFrom("Core.Contracts.dll") }, 
                DuplicateImplementationActions.RegisterMultiple);
container.AutoRegister(new[] { Assembly.LoadFrom("EchoCodeAnalysis.dll") }, 
                DuplicateImplementationActions.RegisterMultiple);
var mi = container.Resolve<IService>();

the contract IService in in Core.Contracts.dll and is reference in the host assembly, this is to give the drag and drop plugin a chance to work without recompilation. In EchoCodeAnalysis.dll we have the actual plugin implementation which is not referenced in the host assembly but share the host of Core.Contracts.dll using the IService.

Core.Contract.dll:

public interface IService
{
    string ID { get; set; }
}

EchoCodeAnalysis.dll:

public class Service : IService
{
    string IService.ID
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }
}

EDIT:

I managed to resolve the first part of my issue.

var type = typeof(IService);
var types = (new[] { Assembly.LoadFrom("EchoCodeAnalysis.dll") }).ToList()
                .SelectMany(s => s.GetTypes())
                .Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList();

container.RegisterMultiple<IService>(types.ToArray());

var mi = container.ResolveAll<Core.Contracts.IService>();

Will fetch and resolve all IService interfaces, that limits the plugin to that interface and not any high up implementations. Say, IMenuItem impltemented as IService, the code above could find any class that is traced back to the origin of IService, but those that explicitly implement IMenuItem which has lets say name, when resolved as IService it will only fetch the IService properties and not include IMenuItem properties. That is where. container.Register(types.ToArray()).AsRespectiveImplementations() would come in handy. But is there anywhere around this issue? or is this a utility that has to be written up to extend TinyIOC?

Edit 2:

We have then moved to a extension but we are still not getting anything resolved.

    public static IEnumerable<T> GetPluginsForModule<T>()
    {
        var type = typeof(T);
        var types = Plugins.SelectMany(s => s.GetTypes())
            .Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList();
        foreach (var t in types)
        {
            if (t.CustomAttributes.Where(x => x.AttributeType == typeof(CluraPlugin)).Any()) 
            {
                CustomAttributeData attr = t.CustomAttributes.Where(x => x.AttributeType == typeof(CluraPlugin)).FirstOrDefault();
                if (attr == null)
                    break;

                string Name = attr.ConstructorArguments.Where(x => x.ArgumentType == typeof(string)).FirstOrDefault().Value as string;
                Type InterfaceTypeArgument = attr.ConstructorArguments.Where(x => x.ArgumentType == typeof(Type)).FirstOrDefault().Value as Type;

                Container.Register(InterfaceTypeArgument, t, Name).AsMultiInstance();
            }
        }
        return Container.ResolveAll(type) as IEnumerable<T>;
    }

I'm passing the correct values, in the Container.Register above we have InterfaceTypeArgument = IMenuItem, t = EchoMenu : IMenuItem, Name = "EchoMenu" but when we ask the container to resolve IMenuItem after registering EchoMenu as its implementation we get null back from resolve all.

Any thoughts?

Clura.NET
  • 11
  • 3
  • is the problem that there is more than one implementation of `IService` and you want to resolve a partucular one or all of them? – NeddySpaghetti May 23 '15 at 03:45
  • There are more than one, but i would expect to get one and eventually a list, all hosted by the same, IService, all classes seem to be registering with the ioc but not mapped to the IService so when requesting a class of IService its unresolved, i could request the service class directly if it was only referenced and the host knew about it, but thats the point with plug and play. – Clura.NET May 23 '15 at 03:49
  • I guess the registration must be failing some how, have you tried using `container.AutoRegister()`, i.e. without any parameters? – NeddySpaghetti May 23 '15 at 04:06
  • I have, but autoregister will only work for the current appdomain without parameters, meaning that the plugin has to be explicitly referenced when compiled :/ – Clura.NET May 23 '15 at 04:11

1 Answers1

0

I found a way, not sure about best practice or potential memory issues down the road, with benchmark testing.

Using:

string PluginDirectory = Directory.GetCurrentDirectory() +"\\Plugins\\";
PluginManager.LoadDirectory(PluginDirectory);
var mi = PluginManager.GetPluginsForModule<IService>();

Which resolves things like this:

public static IEnumerable<object> GetPluginsForModule<T>()
{
    var type = typeof(T);
    var types = Plugins.SelectMany(s => s.GetTypes())
            .Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList();
    foreach (var t in types)
    {
        if (t.CustomAttributes.Where(x => x.AttributeType == typeof(CluraPlugin)).Any()) 
        {
           CustomAttributeData attr = t.CustomAttributes.Where(x => x.AttributeType == typeof(CluraPlugin)).FirstOrDefault();
           if (attr == null)
              break;

           string Name = attr.ConstructorArguments.Where(x => x.ArgumentType == typeof(string)).FirstOrDefault().Value as string;
           Type InterfaceTypeArgument = attr.ConstructorArguments.Where(x => x.ArgumentType == typeof(Type)).FirstOrDefault().Value as Type;

           Container.Register(InterfaceTypeArgument, t, Name).AsMultiInstance();
         }
     }
     return Container.ResolveAll(type);
}

It might not be optimal when doing things on the fly, so there might need to be a need for a plugin manager to hold implementations as instances once you start the application and use them from list of plugin types.

Clura.NET
  • 11
  • 3