我正在尝试将来自UI(DNN模块)的调用记录到它使用的各种服务中,以便分析人们如何与网站进行交互。我正在使用StructureMap 2.5.3.0和Log4Net
我在单个类/实例对上运行良好,但我必须配置这样的东西:
ObjectFactory.Configure(ce =>
ce.ForRequestedType<IRegService>()
.TheDefaultIsConcreteType<RegService>()
.EnrichWith(LoggingEnrichment.InterfaceLogger<IRegService>));
让IRegService
两次感到有些混乱,但我可以忍受它。
日志记录的实现如下:
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());
}
}
这有效,但有几个问题:
我试图通过为StructureMap实现TypeInterceptor来解决这个问题:
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);
}
}
但是我遇到的问题是Process
的调用给了我一个没有实现构建上下文定义的接口的类。这导致必须将InterfaceLogger
的实施更改为
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;
}
永远不会到达return interfaceProxy;
上的断点,这表明context.BuildStack.Current.RequestedType
没有返回正确的界面。奇怪的是,我的所有课程似乎都被正确地注入了。
此外,即使这项工作正常,我仍然只有想要拦截来自UI层的调用的问题。
我正在寻找前两个问题的方法,以及TypeInterceptor
答案 0 :(得分:1)
我通过使用Convention来解决这个问题。以下是我为实现这一目标所采取的措施。
首先,我在指定的程序集上进行了扫描,我将附加装饰器。
x.Scan(scanner =>
{
scanner.Assembly("MyProject.Services"); // Specific assemblyname
scanner.Convention<ServiceRegistrationConvention>();
scanner.WithDefaultConventions();
scanner.LookForRegistries();
});
然后我创建了一个Convention类。我实际上从这个帖子Decorating a generic interface with Structuremap得到了它,并根据你的实现进行了一些修改。
最后这是公约课。
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));
}
}
}
我使用的是同一个LoggingEnrichment类。
希望这能解决你提到的问题。