开发者

external argument injection to method calls in multiple target classes

开发者 https://www.devze.com 2023-04-02 13:47 出处:网络
I have a number of classes that call say Trace.WriteLine(\"LogSomethingClassSpecific\"), in various methods all over the place. Now I would like some of those classes to make all their Trace calls lik

I have a number of classes that call say Trace.WriteLine("LogSomethingClassSpecific"), in various methods all over the place. Now I would like some of those classes to make all their Trace calls like this: Trace.WriteLine("LogSomethingClassSpecific", "CategoryA")

But! I would like to achieve this externally from a single categorizer class; without having to find and modify every Trace call in each of the specific classes.

Sample Architecture

  • Class1,Class2,Class3 which may or may not use Trace calls

  • Separate ControllerClass that makes all the existing Trace calls in say Class2 and Class3 be called with the added argument "CategoryA". While retaining the flexibility to easily change the target classes.

How could that be achieved?

Might it be possible using some Attribute combined with an Aspect Oriented Programming (AOP) library? e.g. the controlling class could specify which classes to target:

[assembly: Categorizer("CategoryA", AttributeTargetTypes = "Namespace.ClassB")]

And then intercept all Trace.WriteLine calls with PostSharp

But I dont know of a way to extract the call context to determine whether the calling class has been marked to include "Categ开发者_高级运维oryA"?

Or are there alternative ways to achieve this aim?

Thankful for any thoughts.


There are a few ways to go about this, but i'm assuming you're calling Trace in places like if/else constructs so here is how you would go about doing what you're asking for (at least, what I think you're asking for)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using PostSharp.Aspects;
    using System.Diagnostics;

    [assembly: ConsoleApplication2.TraceIntercept(AttributeTargetAssemblies = "System", AttributeTargetTypes = "System.Diagnostics.Trace")]

    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                ExampleA ex = new ExampleA();
                ex.Method1();

                Console.ReadKey();
            }

        }

        public class ExampleA
        {
            public void Method1()
            {
                Trace.Write("Test");
            }

        }

[Serializable]
    [TraceIntercept(AttributeExclude = true)]
    public class TraceIntercept : MethodInterceptionAspect
    {
        private bool addArgument;
        private string typeName = string.Empty;

        public override void OnInvoke(MethodInterceptionArgs args)
        {
            CheckInvocationPoint();

            if (addArgument)
            {
                //Do work. Change arguments, etc.
            }

            args.Proceed(); // Proceed with Trace
        }

        private void CheckInvocationPoint()
        {
            if (string.IsNullOrEmpty(this.typeName))
            {
                StackTrace s = new StackTrace();
                StackFrame f = s.GetFrame(2);
                string className = f.GetMethod().DeclaringType.Name;

                if (classsName.Equals("ExampleA"))
                {
                    addArgument = true;
                }
            }
        }
    }
}

This will intercept calls to the System.Diagnostics.Trace methods and will instead invoke the TraceIntercept.OnInvoke method where you can manipulate the Trace invocation. The way this works is PostSharp will simply replace calls to Trace.Write with calls to the aspect.

Edit: As far as I know, there isn't a way to acquire the actual invocation point of the target method. This means that you have to do some reflection at runtime. I've updated the code to use the stack trace. You only have to do this once (per type) since the aspect lifetime is going to be per type not per instance so you only take a hit once. It's not how I would do it, but I assume this will be for debugging purposes.

0

精彩评论

暂无评论...
验证码 换一张
取 消