4

UI(DNNモジュール)から使用するさまざまなサービスへの通話をログに記録して、ユーザーがサイトをどのように操作しているかをプロファイルしようとしています。StructureMap2.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());
    }
}

これは機能しますが、いくつかの問題があります。

  1. 各サービス<->インターフェイスペアを手動で構成する必要があります
  2. UIからサービスが呼び出されたときにのみログを接続したい

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レイヤーからの呼び出しのみをインターセプトしたいという問題があります。

私は私の最初の2つの問題の方法を探しています、そしてまた私が間違っていることTypeInterceptor

4

1 に答える 1

1

コンベンションを使用してこれを回避しました。以下は、これを達成するために私が行った手順です。

最初に、指定したアセンブリをスキャンして、デコレータを取り付けました。

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

                });

次に、Convention クラスを作成しました。私は実際に、このスレッドDecorating a generic interface with Structuremapから取得し、実装に基づいていくつかの改訂を行いました。

そして最後に、これは 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));
            }

        }
    }

あなたが持っているのと同じ LoggingEnrichment クラスを使用します。

これにより、言及された問題が解決されることを願っています。

于 2012-05-15T16:42:38.067 に答える