Intercept an instance using Unity? Globally intercept a specific type

741 Views Asked by At

Im looking for a way to always intercept an instance of a specific class as soon as its properties are used..

For instance if MyTestClass would be intercepted..this would trigger the interception:

var myObj = new MyTestClass();
var x = myObj.SomeProperty;

that would then intercept the method "get_SomeProperty".. however all the examples that I have seen using Unity requires me to "pipe" the creation of the instance of MyTestClass through container.Resolve();.. I would like to avoid that.. is it possible?.. Im pretty sure I have done something like this using Castle.DynamicProxy once before.. but my current application just so happened to have Unity installed so it seamed like a good idea to re-use Unity as far as possible.

Another reason for not using the container.Resolve() is that this instance of mine might as well be created inside an MVC action, which doesnt have a logical reference to the container.. and I dont think that injecting the MyTestClass as a paramter to the constructor would be a very good idea..

Br, Inx

3

There are 3 best solutions below

1
Tim B On

From the docs:

You can use Unity interception as a stand-alone feature with no dependency injection container by using the Intercept class.

The syntax is ugly though.

2
code4life On

Ok, so here goes...

First suppose we have this domain class definition:

public interface IInterceptableClass
{
    string FirstName { get; set; }
    string LastName { get; }
    string GetLastName();
}

public class InterceptableClass : IInterceptableClass
{
    public string FirstName { get; set; }
    public string LastName { get; private set; }

    public InterceptableClass()
    {
        LastName = "lastname";
    }

    public string GetLastName()
    {
        return LastName;
    }
}

And suppose you have a simple interceptor behavior defined like so:

internal class SampleInterceptorBehavior : IInterceptionBehavior
{
    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        // this invokes the method at the tip of the method chain 
        var result = getNext()(input, getNext);

        // method executed with no exceptions (yay)
        if (result.Exception == null)
        {
            //input.Target
            Console.WriteLine($"intercepting: target={input.Target.ToString()}, method={input.MethodBase.Name}");
        }
        else // boo..!
        {
            // handle exception here
            Console.WriteLine($"error! message={result.Exception?.Message}");
        }

        return result;
    }

    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public bool WillExecute { get { return true; } }
}

You would wire it up via Unity like this:

    static void Main(string[] args)
    {
        var container = new UnityContainer();
        container.AddNewExtension<Interception>();
        container.RegisterType<IInterceptableClass, InterceptableClass>(
            new Interceptor<TransparentProxyInterceptor>(),
            new InterceptionBehavior<SampleInterceptorBehavior>());

        var myInstance = container.Resolve<IInterceptableClass>();

        // just want to illustrate that privae sets are not supported...
        myInstance.FirstName = "firstname";
        var lastname = myInstance.GetLastName();

        Console.ReadLine();
    } 

Note that if you don't use Unity to wire up the interception, you would have to do this manually. For one-offs, some devs might prefer it that way, but in practice I've always found that path to be unsustainable, and with multiple intercepts, quite brutal. So always use Unity, if you can.

If you absolutely have to bypass Unity though, here's how you do it:

        var manualInstance = Intercept.ThroughProxy<IInterceptableClass>(
            new InterceptableClass(), // <-- this could be an already-existing instance as well...
            new TransparentProxyInterceptor(), 
            new IInterceptionBehavior[]
            {
                new SampleInterceptorBehavior()
            });

        manualInstance.FirstName = "firstname";
        var lastname = manualInstance.GetLastName();
0
Inx51 On

This is what I ended up doing..

RegisterTypes in UnityConfig (MVC)

  container.RegisterType<IDependencyResolverFactory, DependencyResolverFactory>
                (
                    new InjectionConstructor(container)
                );
container.RegisterType<ISearchModel, SearchModel>(
                new Interceptor(typeof(VirtualMethodInterceptor)),
                new InterceptionBehavior(typeof(SearchModelInterceptionBehaviour)));

IDependencyResolverFactory

public interface IDependencyResolverFactory
    {
        TEntity Resolve<TEntity>();
    }

DependencyResolverFactory public class DependencyResolverFactory : IDependencyResolverFactory { private IUnityContainer _container;

    public DependencyResolverFactory(IUnityContainer container)
    {
        _container = container;
    }

    public TEntity Resolve<TEntity>()
    {
        return _container.Resolve<TEntity>();
    }

    public TEntity Resolve<TEntity>(TEntity type) where TEntity : class
    {
        return (TEntity)_container.Resolve(type.GetType());
    }
}

and then simply in my controller:

    private IDependencyResolverFactory _dependencyResolverFactory;

        public HomeController( IDependencyResolverFactory dependencyResolverFactory)
        {
            _dependencyResolverFactory = dependencyResolverFactory;
        }

public ActionResult Index()
        {
            var x = _dependencyResolverFactory.Resolve<SearchFormViewModel>();
            var p = x.Translations.Age;
            var a = x.Translations.FirstName;
            return View();
        }

hopefully that makes scens to someone except me :P