问题
Thanks in advance for your help! (Yes, there's a question at the bottom)
I'm using Unity 3.x Interception to perform AOP pre and post database connection and transaction activities. The database interceptor is always instantiated and the transaction interceptor is CustomAttributeMatchingRule based, both via InterfaceInterceptor. I have properties that are being set in my TransactionAttribute:
[Transaction(IsolationLevel.ReadUncommitted, NoRollbackFor = new[] { typeof(TestException) })]
as an example I'm using in my unit test. I would like to access them in my TransactionCallHandler class invoke method. I've seen examples saying
var transactionAttribute = input.MethodBase.GetCustomAttribute<TransactionAttribute>(false);
is the way to access this, yet my transaction var is null. My conclusion is the interception proxy class is being checked for the custom attribute and not the original concrete instance.
My work around for this is to reflect all the way back to the class level, dig through to figure out what the correct method is that's being intercepted and perform the get custom attribute from there.
var methods = input
.Target
.GetType()
.GetMethods()
.Where(m => m.Name == input.MethodBase.Name)
.Where(m => m.GetCustomAttribute<TransactionAttribute>(false) != null);
(There's about another 30 lines of code to ensure I'm not accessing the wrong method name if the method has overloads; hence the performance drag...)
So, after all that, my question is: Am I not performing the reflection correctly? Is there a bug in Unity I should report?
Here's my container definitions:
Container = new UnityContainer();
Container.AddNewExtension<Interception>();
Container.RegisterType<IMockUseDefaultConnectionString, MockUseDefaultConnectionString>(
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<InterfaceInterceptor>(),
new InjectionConstructor(new DatabaseSettings()));
Container.RegisterType<IMockUseHardcodedConnectionString, MockUseHardCodedConnectionString>(
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<InterfaceInterceptor>(),
new InjectionConstructor(new DatabaseSettings
{
ConnectionString = MockUseHardCodedConnectionString.ConnectionString
}));
/* IDatabaseSettings is not registered to manually control the settings being used */
var first = new InjectionProperty("Order", 1);
var second = new InjectionProperty("Order", 2);
Container
.Configure<Interception>()
.AddPolicy("DatabaseConnectionPolicy")
.AddMatchingRule<NamespaceMatchingRule>(new InjectionConstructor("MyNamespace.*", true))
.AddCallHandler<DatabaseConnectionCallHandler>(first);
Container
.Configure<Interception>()
.AddPolicy("TransactionPolicy")
.AddMatchingRule(new CustomAttributeMatchingRule(typeof(TransactionAttribute), inherited: false))
.AddCallHandler<TransactionCallHandler>(second);
回答1:
I think the behavior you are seeing is a consequence of the design of the interception methods. When using an InterfaceInterceptor a proxy object is created that implements the target interface however the proxy object is a totally different type from the original type.
If you go with a VirtualMethodInterceptor, which is type compatible, then you should be able to get the custom attribute using your original approach. Of course, the downside of the VirtualMethodInterceptor is that all methods to intercept must be virtual.
回答2:
Another options is to create a subclass from HandlerAttribute. See this SO post for details, or read this Unity doc. In your implemention of CreateHandler() of HandlerAttribute, pass your attribute instance to your call handler instance so that you don't have to call GetCustomAttributes(). Or, implement both HandlerAttribute and ICallHandler in one class and just return this:
public sealed class AuditAttribute : HandlerAttribute, ICallHandler
{
#region attribute properties
public string Key { get; set; } // your own attribute properties
public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
{
return this;
}
#endregion
#region ICallHandler
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
// do stuff ...
// then
return getNext()(input, getNext);
}
public int Order { get; set; }
#endregion
}
回答3:
I was looking through some sample code and I noticed something quite simple, that I hadn't thought of.
When the call handler is created by the attribute, pass the parameters you want into the constructor of the handler. Then they are in the handler instance that is being used for the attribute. Problem solved.
来源:https://stackoverflow.com/questions/17222691/unity-interception-getcustomattribute