How to resolve type using ServiceStack Funq IoC
Asked Answered
M

3

2

I'm trying to write a JobActivator for HangFire using ServiceStack IoC and I'm having trouble resolving from a type. I'm sure this will be an easy answer for someone with more experience with generics.

The container I'm passing in is coming from HostContext.Container

using Hangfire;
using System;
using System.Collections.Generic;
using System.Linq;
using ServiceStack;

namespace Common.Hangfire
{

    public class FunqJobActivator : JobActivator
    {

        private Funq.Container _container;

        public FunqJobActivator(Funq.Container container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            _container = container;
        }

        public override object ActivateJob(Type type)
        {
            return _container.Resolve<type>();  //** this doesn't compile
        }
    }
}
Magneto answered 21/4, 2015 at 13:57 Comment(1)
The choice of Funq as your IoC framework is unfortunate in this case. Many IoC frameworks will offer a Resolve method that accepts a Type as its argument. I don't believe Funq does. It relies on a generic method argument to get to the type.Eduard
E
3

Whilst Funq is a typed IOC with Generic API's, you can add a helper extension method to enable resolving instances using a runtime type, e.g:

public static class ContainerExtensions
{
    public static object TryResolve(this Container container, Type type)
    {
        var mi = typeof(Container).GetMethods(BindingFlags.Public | BindingFlags.Instance)
            .First(x => x.Name == "TryResolve" &&
                   x.GetGenericArguments().Length == 1 &&
                   x.GetParameters().Length == 0);

        var genericMi = mi.MakeGenericMethod(type);
        var instance = genericMi.Invoke(container, new object[0]);
        return instance;
    }
}

Which will allow you to resolve registered dependencies using a runtime Type, e.g:

var container = new Container();
container.Register(c => new Foo());

var instance = container.TryResolve(typeof(Foo));
Eglanteen answered 21/4, 2015 at 14:58 Comment(2)
Thanks @Eglanteen that worked. Since I'm resolving this outside of ServiceStack services, should I call _container.AutoWire(instance) after instance is resolved?Magneto
@Magneto This is the same as calling the TryResolve<T> generic method directly. How it's resolved doesn't affect how it's registered, i.e. the dependency will be in the same state regardless of which API it's resolved with.Eglanteen
E
2

I would recommend using a different IoC framework, because Funq does not support a resolve method that takes a Type argument, i.e. it does not have a method

object Container.Resolve(Type theType);

Thus the marriage with Hangfire is a difficult one, since Hangfire does not provide an overload that you could use as:

virtual T ActivateJob<T>() where T : class

If you insist on using Funq, this can be (inefficiently) solved like this:

public class FunqJobActivator : JobActivator
{
    private const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
    private static readonly _activateMethod = 
        typeof(FunqJobActivator).GetMethod("InternalActivateJob", flags);

    private Funq.Container _container;

    public FunqJobActivator(Funq.Container container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
    }

    public override object ActivateJob(Type type)
    {
        // this will allow calling InternalActivateJob<T> with typeof(T) == type.
        var method = _activateMethod.MakeGenericMethod(new [] { type });
        return method.Invoke(this, null);
    }

    private object InternalActivateJob<T>() where T : class
    {
        return _container.Resolve<T>();
    }
}
Eduard answered 21/4, 2015 at 14:24 Comment(2)
Thanks @Alex, since I'm using ServiceStack here, I don't want to add another IoC since that is built-in.Magneto
@Magneto ok, then I guess you will have to use the reflection based approach as in the above code block.Eduard
T
0

I think you want to get a T and to return a T

public T ActivateJob<T>() where T : class
{
    return _container.Resolve<T>();  
}
Tenuous answered 21/4, 2015 at 14:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.