I want to register all classes that implements a specific interface into Unity with the WithMappings.FromMatchingInterface
convention. In addition, I want all of the registered objects to be intercepted using an interface interception behaviour. The problem is that Unity also registers mappings between the concrete classes, and when those classes are resolved, an exception is thrown with the message:
"[type] is not interceptable"
I realise that resolving an object using the concrete class type is not best practice but I wonder why Unity automatically adds mappings for both interface -> concrete class as well as concrete class -> concrete class when registering by convention? This means that it will never work if you add an interface interceptor and resolve using the concrete type.
My desired outcome for this would be that Unity did not add the concrete type -> concrete type mappings when registering by convention and giving it an interface interceptor, that way we could resolve the class using its concrete type if we wanted to, we would just not get interception.
I do not want to use the VirtualMethodInterceptor
as I do not want to impose changes to the classes in order for interception to work, this includes inheriting from MarshalByRef
. I also want to avoid individually registering all the objects.
My questions is thus, how do I register just the interface mappings when registering by convention?
Update : registering the classes individually gives the same problem, so the assumption is therefore that once an object is registered with an interfaceinterceptor then it cannot be resolved by using the concrete type.
The new registration code:
container.RegisterType<ISomeService, SomeService>(new InjectionMember[]
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<TraceInterceptor>()
});
container.RegisterType<ISomeRepository, SomeRepository>(new InjectionMember[]
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<TraceInterceptor>()
});
Update 2 Adding a default interceptor for all the interfaces seems to work although this solution is rather hacky. This solution requires a little code just before the standard registration by convention, and removal of the InterfaceInterceptor
in the conventions-based registration.
Pre-registration code
foreach (var type in types)
{
container
.Configure<Interception>()
.SetDefaultInterceptorFor(type.GetInterface("I" + type.Name), new InterfaceInterceptor());
}
Some code that explains the dilemma :
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
using System.Diagnostics;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();
var types = AllClasses.FromAssemblies(typeof(ISomeService).Assembly).Where(type => type == typeof(SomeService) || type == typeof(SomeRepository));
container.RegisterTypes(
types,
WithMappings.FromMatchingInterface,
getLifetimeManager: WithLifetime.ContainerControlled,
getInjectionMembers: c => new InjectionMember[]
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<TraceInterceptor>()
});
// this works fine, the interceptor does what it is supposed to.
var someService1 = container.Resolve<ISomeService>();
someService1.SomeServiceMethod("Hello from main method");
// should I by any chance resolve using the concrete service directly, it has a meltdown due to the interface interceptor.
var someService2 = container.Resolve<SomeService>();
someService2.SomeServiceMethod("This will never be shown due to a hissy fit thrown by the container about the concrete SomeService is not interceptable.");
}
}
public class TraceInterceptor : IInterceptionBehavior
{
public System.Collections.Generic.IEnumerable<System.Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Trace.WriteLine(string.Format("Hello from trace interception behavior for type [{0}]!", input.Target.GetType().FullName));
return getNext().Invoke(input, getNext);
}
public bool WillExecute
{
get { return true; }
}
}
public interface ISomeService
{
string SomeServiceMethod(string someParameter);
}
public class SomeService : ISomeService
{
private ISomeRepository _someRepository;
public SomeService(ISomeRepository someRepository)
{
_someRepository = someRepository;
}
public string SomeServiceMethod(string someParameter)
{
return _someRepository.SomeRepositoryMethod(someParameter);
}
}
public interface ISomeRepository
{
string SomeRepositoryMethod(string someParameter);
}
public class SomeRepository : ISomeRepository
{
public string SomeRepositoryMethod(string someParameter)
{
Trace.WriteLine(someParameter);
return "Hello from repository!";
}
}
}