Attempting to do a registration hook for StructureMap using DynamicProxy

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

+4
source share
1 answer

I communicated with this using the Convention. Below are the steps I took to achieve this.

First, I performed a scan on my specified assembly, where I would attach my decorator.

 x.Scan(scanner => { scanner.Assembly("MyProject.Services"); // Specific assemblyname scanner.Convention<ServiceRegistrationConvention>(); scanner.WithDefaultConventions(); scanner.LookForRegistries(); }); 

Then I created a convention class. I really got it from this topic Finishing a common interface using Structuremap and did some revision based on your implementation.

And finally, this is the class of the Convention.

  public class ServiceRegistrationConvention : IRegistrationConvention { public void Process(Type type, Registry registry) { var handlerInterfaces = (from t in type.GetInterfaces() where (t.Namespace.StartsWith("MyProject.UIServices", StringComparison.OrdinalIgnoreCase) || t.Namespace.StartsWith("MyProject.Services", StringComparison.OrdinalIgnoreCase)) select t); foreach (var handler in handlerInterfaces) { registry.For(handler) .EnrichWith((ctx, orig) => LoggingEnrichment.InterfaceLogger(handler, orig)); } } } 

I use the same LoggingEnrichment class that you have.

Hope this solves your mentioned problems.

+1
source

Source: https://habr.com/ru/post/1338725/


All Articles