質問

I have Unity setup to intercept method calls when the method is decorated with a specific attribute. I want all Unity proxy objects to persist within each thread (rather than be transient).

The issue is that the object created by the decoration attribute is created 'new' each time. I cannot see a way to access the UnityContainer from within the attribute. If I could, I would create the LogHandler as a per-thread entity and request it via Unity. (Does this even make sense though? Using Unity to resolve objects used in unity interception?).

If you run this code, the count output by the logger is always '1'. To be clear, this 'LogHandler' is what I want to persist.

How do you resolve objects via unity elsewhere in your code? Do you pass around the unity container? Is there a pattern I can use to request it from anywhere in my code?

using System;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace UnityTest2
{
    class Program
    {
        private static UnityContainer _uC;

        static void Main(string[] args)
        {
            _uC = new UnityContainer();
            _uC.AddNewExtension<Interception>();

            _uC.Configure<Interception>().SetInterceptorFor<ICalc>(new InterfaceInterceptor());
            _uC.RegisterType<ICalc, Calc>( new PerThreadLifetimeManager() );
            var c = _uC.Resolve<ICalc>();
            Console.WriteLine(c.Add(3, 7));
            Console.WriteLine(c.Sub(3, 7));
            Console.ReadKey();
        }
    }

    public class LogAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            // I want this to persist per thread
            return new LogHandler();
        }
    }

    public class LogHandler : ICallHandler
    {
        private int runCount = 0;
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {

            runCount++;

            // this is always '1'
            Console.WriteLine(runCount);
            return getNext()(input, getNext);
        }

        public int Order { get; set; }
    }

    public interface ICalc
    {
        int Add(int x, int y);
        int Sub(int x, int y);
        int Mul(int x, int y);
    }

    public class Calc : ICalc
    {
        [Log]
        public int Add(int x, int y)
        {
            return x + y;
        }
        [Log]
        public int Sub(int x, int y)
        {
            return x - y;
        }

        public int Mul(int x, int y)
        {
            return x * y;
        }
    }
}
役に立ちましたか?

解決

You can register your call handler with Unity with a PerThreadLifetimeManager during startup.

_uC.RegisterType<LoggingCallHandler>(new PerThreadLifetimeManager());

Then the attribute can resolve the handler from the container:

public class LoggingAttribute : HandlerAttribute
{        
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return container.Resolve<LoggingCallHandler>();
    }
}

Usually, you don't want to pass around instances of the container since this tightly couples your application to the specific container used. This would be using the container as a Service Locator. Many people consider this to be an anti-pattern.

Call handler attributes are part of the Unity infrastructure (for your application); they extend Unity's abstract HandlerAttribute and require the CreateHandler method to accept an IUnityContainer so using the container in the HandlerAttribute is not unexpected.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top