Reputation: 13233
What I would like to achieve is:
[Factory]
public interface IFooFactory
{
Foo Create();
}
unityContainer.RegisterType<IFooFactory>(
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<FactoryInterceptionBehavior>());
Where there is no implementation of IFooFactory
- because it's provided by FactoryInterceptionBehavior
.
When i try to resolve the IFooFactory
however, i get a ResolutionFailedException
with message:
InvalidOperationException - The current type, IFooFactory, is an interface and cannot be constructed. Are you missing a type mapping?
I also thought about creating the proxy myself (using Intercept.ThroughProxy<>
or castle dynamic proxy...), but I still need to do type registration with the container. I don't know how to delegate/override the instantiation of such a type (like Ninject's Bind<IFoo>().ToMethod(() => return new Foo())
).
Upvotes: 4
Views: 2437
Reputation: 13233
So after some research and trial & error i've found out, that the Unity.Interception does not support proxies of interfaces where there is no actual class implementing the interface and where invocations finally end up (castle dynamic proxies calls them "interface proxy without target").
So what i did is using is Castle.Core dynamic proxy in conjunction with unity's out-of-the-box InjectionFactory
(which can be used to delegate resolution to a Func
factory).
The Injection Factory looks like this:
var proxyFuncFactory = new InjectionFactory(CreateProxy);
private static object CreateProxy(IUnityContainer container, Type interfaceType, string name)
{
var proxyGenerator = container.Resolve<Castle.DynamicProxy.ProxyGenerator>();
return proxyGenerator.CreateInterfaceProxyWithoutTarget(interfaceType, container.Resolve<AutoGeneratedFactoryInterceptor>());
}
and can be used in a binding like this:
IUnityContainer.RegisterType<ISomeFactory>(proxyFuncFactory);
The AutoGeneratedFactoryInterceptor
looks like:
internal class AutoGeneratedFactoryInterceptor : IInterceptor
{
private readonly IUnityContainer _unityContainer;
public AutoGeneratedFactoryInterceptor(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public void Intercept(IInvocation invocation)
{
IEnumerable<ResolverOverride> resolverOverrides = DetermineResolverOverrides(invocation);
Type typeToResolve = DetermineTypeToResolve(invocation.Method);
invocation.ReturnValue = _unityContainer.Resolve(typeToResolve, resolverOverrides.ToArray());
}
private static Type DetermineTypeToResolve(MethodInfo method)
{
ResolveToAttribute resolveToAttribute = method.Attribute<ResolveToAttribute>();
if (resolveToAttribute == null)
{
return method.ReturnType;
}
if (resolveToAttribute.ResolveTo.IsGenericTypeDefinition)
{
return resolveToAttribute.ResolveTo.MakeGenericType(method.GetGenericArguments());
}
return resolveToAttribute.ResolveTo;
}
private static IEnumerable<ResolverOverride> DetermineResolverOverrides(IInvocation invocation)
{
return invocation.Method.Parameters()
.Select((parameterInfo, parameterIndex) =>
new ParameterOverride(parameterInfo.Name, invocation.Arguments[parameterIndex]));
}
It matches factory-method-argument to constructor-arguments by name (Unity out-of-the-box ParameterOverride
). Note, that especially the generic parameter support is not perfect. It supports the following usages:
public interface IFooFactory
{
Foo Create();
}
and
unityContainer.RegisterType(typeof(IFoo<>), typeof(Foo<>));
public interface IFooFactory
{
IFoo<T> Create<T>();
}
and
public interface IFooFactory
{
Foo Create(string parameter1, object parameter2);
}
and
public interface IFooFactory
{
[ResolveTo(typeof(Foo))]
IFoo Create();
}
as well as
public interface IFooFactory
{
[ResolveTo(typeof(Foo<>))]
IFoo Create<T>();
}
Also note, that any constructor-arguments of the resolved (created) instance that are not covered by ParameterOverride
's are ctor-inject "as usual".
Upvotes: 1