Es sieht nicht so aus, als gäbe es einen einfachen Erweiterungspunkt, aber ich habe es mit einer ziemlich anständigen Lösung mit einer benutzerdefinierten Konvention arbeiten. Um Ihnen zu helfen, die Entscheidungen zu verstehen, die ich getroffen habe, werde ich Sie durch einige Schritte führen (die vielen, vielen Fehltritte überspringend, die ich auf meinem Weg gemacht habe).
Lassen Sie uns zuerst die DefaultConvention ansehen, die Sie bereits verwenden.
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));
}
}
Ganz einfach, wir die Art und Interface-Paare erhalten und prüfen Sie, ob sie einen Konstruktor haben, wenn sie es tun wir sie registrieren. Es wäre schön, dies einfach so zu ändern, dass es DecorateWith aufruft, aber Sie können das nur für <>() aufrufen. Verwenden Sie <>(), nicht For(). Verwenden Sie().
Als nächstes sehen wir uns an, was DecorateWith tut:
public T DecorateWith(Expression<Func<TPluginType, TPluginType>> handler)
{
this.AddInterceptor((IInterceptor) new FuncInterceptor<TPluginType>(handler, (string) null));
return this.thisInstance;
}
So entsteht ein FuncInterceptor und registriert sie. Ich habe ein gutes Stück Zeit versucht, eine dieser dynamisch mit Reflexion zu schaffen, bevor sie entscheidet, es würde nur einfacher sein, eine neue Klasse zu machen:
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());
}
}
Diese Klasse nur macht es einfacher, mit zu arbeiten, wenn wir die Art haben als eine Variable.
Endlich habe ich meine eigene Konvention basierend auf der Standard Konvention gemacht.
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);
}
}
Es ist fast genau das gleiche mit einem Zusatz, erstelle ich einen Abfangjäger und interceptorType für jede Art wir registrieren. Ich registriere diese Politik dann.
Abschließend einige Unit-Tests, es zu beweisen funktioniert:
[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
{
}
ist es auf jeden Fall Raum für einige hier aufzuräumen (Caching, machen es trocken, mehr Tests machen es einfacher zu konfigurieren), aber es funktioniert für was Sie brauchen und ist eine ziemlich vernünftige Art, es zu tun.
Bitte fragen Sie, wenn Sie weitere Fragen dazu haben.
Haben Sie das zur Arbeit gebracht? –
Leider nicht. Ich habe sie manuell für jeden meiner Plugin-Typen hinzugefügt. – rinat