Ответ 1
Не похоже на то, что для этого есть простая точка расширения, но я получил работу с довольно приличным решением с использованием специального соглашения. Чтобы помочь вам понять решения, которые я принял, я проведу вас через несколько шагов (пропуская много и много ошибок, которые я сделал на моем пути).
Сначала давайте посмотрим на DefaultConvention, который вы уже используете.
DefaultConvention:
public class DefaultConventionScanner : ConfigurableRegistrationConvention
{
public override void Process(Type type, Registry registry)
{
if (!TypeExtensions.IsConcrete(type))
return;
Type pluginType = this.FindPluginType(type);
if (pluginType == null || !TypeExtensions.HasConstructors(type))
return;
registry.AddType(pluginType, type);
this.ConfigureFamily(registry.For(pluginType, (ILifecycle)null));
}
public virtual Type FindPluginType(Type concreteType)
{
string interfaceName = "I" + concreteType.Name;
return Enumerable.FirstOrDefault<Type>((IEnumerable<Type>)concreteType.GetInterfaces(), (Func<Type, bool>)(t => t.Name == interfaceName));
}
}
Довольно просто, мы получаем пары типов и интерфейсов и проверяем, есть ли у них конструктор, если они их регистрируют. Было бы неплохо просто изменить это так, чтобы он вызывал DecorateWith, но вы можете называть это только для параметра "Для < > (). Используйте < > (), а не For(). Используйте().
Далее рассмотрим, что делает DecorateWith:
public T DecorateWith(Expression<Func<TPluginType, TPluginType>> handler)
{
this.AddInterceptor((IInterceptor) new FuncInterceptor<TPluginType>(handler, (string) null));
return this.thisInstance;
}
Итак, это создает FuncInterceptor и регистрирует его. Я потратил довольно много времени, пытаясь создать один из них динамически с отражением, прежде чем решить, что проще будет создать новый класс:
public class ProxyFuncInterceptor<T> : FuncInterceptor<T> where T : class
{
public ProxyFuncInterceptor() : base(x => MakeProxy(x), "")
{
}
protected ProxyFuncInterceptor(Expression<Func<T, T>> expression, string description = null)
: base(expression, description)
{
}
protected ProxyFuncInterceptor(Expression<Func<IContext, T, T>> expression, string description = null)
: base(expression, description)
{
}
private static T MakeProxy(T instance)
{
var proxyGenerator = new ProxyGenerator();
return proxyGenerator.CreateInterfaceProxyWithTarget(instance, new LogInterceptor());
}
}
Этот класс просто упрощает работу, когда у нас есть тип как переменная.
Наконец, я сделал свою собственную конвенцию на основе соглашения по умолчанию.
public class DefaultConventionWithProxyScanner : ConfigurableRegistrationConvention
{
public override void Process(Type type, Registry registry)
{
if (!type.IsConcrete())
return;
var pluginType = this.FindPluginType(type);
if (pluginType == null || !type.HasConstructors())
return;
registry.AddType(pluginType, type);
var policy = CreatePolicy(pluginType);
registry.Policies.Interceptors(policy);
ConfigureFamily(registry.For(pluginType));
}
public virtual Type FindPluginType(Type concreteType)
{
var interfaceName = "I" + concreteType.Name;
return concreteType.GetInterfaces().FirstOrDefault(t => t.Name == interfaceName);
}
public IInterceptorPolicy CreatePolicy(Type pluginType)
{
var genericPolicyType = typeof(InterceptorPolicy<>);
var policyType = genericPolicyType.MakeGenericType(pluginType);
return (IInterceptorPolicy)Activator.CreateInstance(policyType, new object[]{CreateInterceptor(pluginType), null});
}
public IInterceptor CreateInterceptor(Type pluginType)
{
var genericInterceptorType = typeof(ProxyFuncInterceptor<>);
var specificInterceptor = genericInterceptorType.MakeGenericType(pluginType);
return (IInterceptor)Activator.CreateInstance(specificInterceptor);
}
}
Это почти то же самое с одним добавлением, я создаю перехватчик и interceptorType для каждого типа, который мы регистрируем. Затем я зарегистрирую эту политику.
Наконец, несколько модульных тестов, чтобы доказать, что это работает:
[TestFixture]
public class Try4
{
[Test]
public void Can_create_interceptor()
{
var type = typeof (IServiceA);
Assert.NotNull(new DefaultConventionWithProxyScanner().CreateInterceptor(type));
}
[Test]
public void Can_create_policy()
{
var type = typeof (IServiceA);
Assert.NotNull(new DefaultConventionWithProxyScanner().CreatePolicy(type));
}
[Test]
public void Can_register_normally()
{
var container = new Container();
container.Configure(x => x.Scan(y =>
{
y.TheCallingAssembly();
y.WithDefaultConventions();
}));
var serviceA = container.GetInstance<IServiceA>();
Assert.IsFalse(ProxyUtil.IsProxy(serviceA));
Console.WriteLine(serviceA.GetType());
}
[Test]
public void Can_register_proxy_for_all()
{
var container = new Container();
container.Configure(x => x.Scan(y =>
{
y.TheCallingAssembly();
y.Convention<DefaultConventionWithProxyScanner>();
}));
var serviceA = container.GetInstance<IServiceA>();
Assert.IsTrue(ProxyUtil.IsProxy(serviceA));
Console.WriteLine(serviceA.GetType());
}
[Test]
public void Make_sure_I_wait()
{
var container = new Container();
container.Configure(x => x.Scan(y =>
{
y.TheCallingAssembly();
y.Convention<DefaultConventionWithProxyScanner>();
}));
var serviceA = container.GetInstance<IServiceA>();
serviceA.Wait();
}
}
}
public interface IServiceA
{
void Wait();
}
public class ServiceA : IServiceA
{
public void Wait()
{
Thread.Sleep(1000);
}
}
public interface IServiceB
{
}
public class ServiceB : IServiceB
{
}
Здесь определенно есть место для очистки (кэширование, сделать его СУХОЙ, больше тестов, упростить настройку), но оно работает для того, что вам нужно, и это довольно разумный способ сделать это.
Спросите, есть ли у вас какие-либо другие вопросы.