I am trying to register calls from the user interface (DNN module) with some of the various services that he uses to find out how people interact with the site. I am using StructureMap 2.5.3.0 and Log4Net
I worked well on individual pairs of classes / instances, but I need to configure things like this:
ObjectFactory.Configure(ce => ce.ForRequestedType<IRegService>() .TheDefaultIsConcreteType<RegService>() .EnrichWith(LoggingEnrichment.InterfaceLogger<IRegService>));
If IRegService twice felt a little dirty, but I can live with it.
Logging is performed as follows:
public class LoggingEnrichment { public static object InterfaceLogger<TInterface>(object concrete) { return InterfaceLogger(typeof(TInterface), concrete); } public static object InterfaceLogger(Type iinterface, object concrete) { var dynamicProxy = new ProxyGenerator(); return dynamicProxy.CreateInterfaceProxyWithTarget(iinterface, concrete, new LogInterceptor()); } } public class LogInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { var watch = new Stopwatch(); watch.Start(); invocation.Proceed(); watch.Stop(); ILog logger = LogManager.GetLogger(typeof(LogInterceptor)); var sb = new StringBuilder(); sb.AppendFormat("Calling: {0}.{1}\n", invocation.InvocationTarget.GetType(), invocation.MethodInvocationTarget.Name); var param = invocation.Method.GetParameters(); if (param.Length > 0) sb.Append("With:\n"); for (int i = 0; i < param.Length; i++) { sb.AppendFormat("\t{0}\n\t\t{1}", param[i].Name, invocation.GetArgumentValue(i)); } if(invocation.Method.ReturnType != typeof(void)) { sb.AppendFormat("Returning: {0}\n", invocation.ReturnValue ?? "null"); } sb.AppendFormat("In: {0}ms\n", watch.ElapsedMilliseconds); logger.Debug(sb.ToString()); } }
This works, but has a couple of problems:
- I need to manually configure each pair of service interfaces ↔
- I only want to enable logging when the service is called from the user interface
I tried to get around this by running TypeInterceptor for StructureMap:
public class ApplicationRegistry : Registry { public ApplicationRegistry() { RegisterInterceptor(new LoggingInterceptor()); Scan(scanner => { scanner.TheCallingAssembly(); var codeBase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", String.Empty); codeBase = codeBase.Substring(0, codeBase.LastIndexOf("/")); scanner.AssembliesFromPath(codeBase); scanner.WithDefaultConventions(); scanner.LookForRegistries(); }); } } public class LoggingInterceptor :TypeInterceptor { public object Process(object target, IContext context) { var newTarget = target; if (context.BuildStack.Current != null && context.BuildStack.Current.RequestedType != null) { newTarget = LoggingEnrichment.InterfaceLogger(context.BuildStack.Current.RequestedType, target); } return newTarget; } public bool MatchesType(Type type) { return type.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase); } }
But I have a problem with the fact that calling Process gives me a class that does not implement the interface defined by the build context. This led to the need to change the implementation of InterfaceLogger to
public static object InterfaceLogger(Type iinterface, object concrete) { if(!iinterface.IsAssignableFrom(concrete.GetType())) return concrete; var dynamicProxy = new ProxyGenerator(); var interfaceProxy = dynamicProxy.CreateInterfaceProxyWithTarget(iinterface, concrete, new LogInterceptor()); return interfaceProxy; }
Breakpoint on return interfaceProxy; never reached, this means that context.BuildStack.Current.RequestedType does not return the right interface. It is strange that all my classes seem to be being entered correctly.
Also, even if this works, I still only have to intercept calls from the user interface layer.
I am looking for the way my first 2 problems are, as well as what I am doing wrong with TypeInterceptor