2013-02-26 88 views
2

我想裝飾我的服務與攔截屬性,然後有基於約定的綁定爲我設置攔截器。我不希望我的屬性繼承攔截屬性...如果我可以避免它。Ninject約定和攔截

例如,我有下面的類:

[Log] 
public class SomeClassToLog 
{ 
    public void DoSomething() { ... } 
} 

我明白我可以綁定這個如下:

var kernel = new StandardKernel(); 
      kernel.Bind(x => x.FromAssembliesMatching("SomeProject.*") 
           .SelectAllClasses() 
           .WithAttribute(typeof(LogAttribute)) 
           .BindToSelf().Configure(syntax => syntax.Intercept().With(LogInterceptor))); 

我怎樣才能做到這一點與屬性和攔截器的不同組合?例如:

如果我有日誌和授權屬性,我將不得不配置3組綁定? (1表示沒有授權的日誌,1表示沒有日誌的授權,一個表示日誌和授權)。

更新:雖然根據我原來的問題參數找不到解決方案,但我偶然發現了一個類似的問題,導致我找到了解決方案。這裏是源代碼:

注:Common.Interception.Interceptors命名空間是在具有到Ninject.Extensions.Interception基準(及其所有需要的依賴的)的組件。我的屬性在單獨的程序集中定義,並且不依賴於它們自己的屬性。

MiscExtensions.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Text; 
using System.Threading.Tasks; 

namespace Common.Interception.Interceptors 
{ 
    // TODO: Remove the dependence on these eventually 

    /// <summary> 
    /// A bundle of extension methods which I needed to borrow from ninject source since they were internal: 
    /// Ninject.Extensions.Interception.Infrastructure.Language 
    /// ExtensionsForIEnumerable 
    /// ExtensionsForMethodInfo 
    /// ExtensionsForICustomAttributeProvider 
    /// </summary> 
    internal static class MiscExtensions 
    { 
     /// <summary> 
     /// Converts all of the items in the specified series using the specified converter. 
     /// </summary> 
     /// <typeparam name="TInput">The type of items contained in the input list.</typeparam> 
     /// <typeparam name="TOutput">The type of items to return.</typeparam> 
     /// <param name="items">The series of items to convert.</param> 
     /// <param name="converter">The converter to use to convert the items.</param> 
     /// <returns>A list of the converted items.</returns> 
     public static IEnumerable<TOutput> Convert<TInput, TOutput>(this IEnumerable<TInput> items, 
                    Func<TInput, TOutput> converter) 
     { 
      return items.Select(converter); 
     } 

     /// <summary> 
     /// Skips the last items where the count of skipped items is given by count. 
     /// </summary> 
     /// <typeparam name="T">The type of the enumerable.</typeparam> 
     /// <param name="source">The source.</param> 
     /// <param name="count">The count of skipped items.</param> 
     /// <returns>An enumerable that skippes the last items from the source enumerable.</returns> 
     public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count) 
     { 
      var enumerator = source.GetEnumerator(); 
      var items = new Queue<T>(); 

      while (enumerator.MoveNext()) 
      { 
       if (count-- <= 0) 
       { 
        yield return items.Dequeue(); 
       } 

       items.Enqueue(enumerator.Current); 
      } 
     } 

     private const BindingFlags DefaultBindingFlags = 
      BindingFlags.Public | 
      BindingFlags.NonPublic | 
      BindingFlags.Instance; 

     public static PropertyInfo GetPropertyFromMethod(this MethodInfo method, Type implementingType) 
     { 
      if (!method.IsSpecialName) 
      { 
       return null; 
      } 

      var isGetMethod = method.Name.Substring(0, 3) == "get"; 
      var returnType = isGetMethod ? method.ReturnType : method.GetParameterTypes().Last(); 
      var indexerTypes = isGetMethod ? method.GetParameterTypes() : method.GetParameterTypes().SkipLast(1); 

      return implementingType.GetProperty(method.Name.Substring(4), DefaultBindingFlags, null, returnType, indexerTypes.ToArray(), null); 
     } 

     public static PropertyInfo GetPropertyFromMethod(this MethodInfo method) 
     { 
      if (!method.IsSpecialName) 
      { 
       return null; 
      } 
      return method.DeclaringType.GetProperty(method.Name.Substring(4), DefaultBindingFlags); 
     } 

     /// <summary> 
     /// Gets the types of the parameters of the method. 
     /// </summary> 
     /// <param name="method">The method in question.</param> 
     /// <returns>An array containing the types of the method's parameters.</returns> 
     public static IEnumerable<Type> GetParameterTypes(this MethodBase method) 
     { 
      return method.GetParameters().Convert(p => p.ParameterType); 
     } 

     /// <summary> 
     /// Gets the method handle of either the method or its generic type definition, if it is 
     /// a generic method. 
     /// </summary> 
     /// <param name="method">The method in question.</param> 
     /// <returns>The runtime method handle for the method or its generic type definition.</returns> 
     public static RuntimeMethodHandle GetMethodHandle(this MethodBase method) 
     { 
      var mi = method as MethodInfo; 

      if (mi != null && 
       mi.IsGenericMethod) 
      { 
       return mi.GetGenericMethodDefinition().MethodHandle; 
      } 
      return method.MethodHandle; 
     } 

     /// <summary> 
     /// Gets the first attribute of a specified type that decorates the member. 
     /// </summary> 
     /// <typeparam name="T">The type of attribute to search for.</typeparam> 
     /// <param name="member">The member to examine.</param> 
     /// <returns>The first attribute matching the specified type.</returns> 
     public static T GetOneAttribute<T>(this ICustomAttributeProvider member) 
      where T : Attribute 
     { 
      var attributes = member.GetCustomAttributes(typeof(T), true) as T[]; 

      return (attributes == null) || 
        (attributes.Length == 0) 
         ? null 
         : attributes[0]; 
     } 

     /// <summary> 
     /// Gets the first attribute of a specified type that decorates the member. 
     /// </summary> 
     /// <param name="member">The member to examine.</param> 
     /// <param name="type">The type of attribute to search for.</param> 
     /// <returns>The first attribute matching the specified type.</returns> 
     public static object GetOneAttribute(this ICustomAttributeProvider member, Type type) 
     { 
      object[] attributes = member.GetCustomAttributes(type, true); 

      return (attributes == null) || 
        (attributes.Length == 0) 
         ? null 
         : attributes[0]; 
     } 

     /// <summary> 
     /// Gets an array of attributes matching the specified type that decorate the member. 
     /// </summary> 
     /// <typeparam name="T">The type of attribute to search for.</typeparam> 
     /// <param name="member">The member to examine.</param> 
     /// <returns>An array of attributes matching the specified type.</returns> 
     public static T[] GetAllAttributes<T>(this ICustomAttributeProvider member) 
      where T : Attribute 
     { 
      return member.GetCustomAttributes(typeof(T), true) as T[]; 
     } 

     /// <summary> 
     /// Gets an array of attributes matching the specified type that decorate the member. 
     /// </summary> 
     /// <param name="member">The member to examine.</param> 
     /// <param name="type">The type of attribute to search for.</param> 
     /// <returns>An array of attributes matching the specified type.</returns> 
     public static object[] GetAllAttributes(this ICustomAttributeProvider member, Type type) 
     { 
      return member.GetCustomAttributes(type, true); 
     } 

     /// <summary> 
     /// Determines whether the member is decorated with one or more attributes of the specified type. 
     /// </summary> 
     /// <typeparam name="T">The type of attribute to search for.</typeparam> 
     /// <param name="member">The member to examine.</param> 
     /// <returns><see langword="True"/> if the member is decorated with one or more attributes of the type, otherwise <see langword="false"/>.</returns> 
     public static bool HasAttribute<T>(this ICustomAttributeProvider member) 
      where T : Attribute 
     { 
      return member.IsDefined(typeof(T), true); 
     } 

     /// <summary> 
     /// Determines whether the member is decorated with one or more attributes of the specified type. 
     /// </summary> 
     /// <param name="member">The member to examine.</param> 
     /// <param name="type">The type of attribute to search for.</param> 
     /// <returns><see langword="True"/> if the member is decorated with one or more attributes of the type, otherwise <see langword="false"/>.</returns> 
     public static bool HasAttribute(this ICustomAttributeProvider member, Type type) 
     { 
      return member.IsDefined(type, true); 
     } 

     /// <summary> 
     /// Determines whether the member is decorated with an attribute that matches the one provided. 
     /// </summary> 
     /// <typeparam name="T">The type of attribute to search for.</typeparam> 
     /// <param name="member">The member to examine.</param> 
     /// <param name="attributeToMatch">The attribute to match against.</param> 
     /// <returns><see langword="True"/> if the member is decorated with a matching attribute, otherwise <see langword="false"/>.</returns> 
     public static bool HasMatchingAttribute<T>(this ICustomAttributeProvider member, T attributeToMatch) 
      where T : Attribute 
     { 
      T[] attributes = member.GetAllAttributes<T>(); 

      if ((attributes == null) || 
       (attributes.Length == 0)) 
      { 
       return false; 
      } 

      return attributes.Any(attribute => attribute.Match(attributeToMatch)); 
     } 
    } 
} 

AlternateInterceptorRegistrationStrategy.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using Ninject; 
using Ninject.Components; 
using Ninject.Extensions.Interception; 
using Ninject.Extensions.Interception.Advice; 
using Ninject.Extensions.Interception.Planning.Directives; 
using Ninject.Extensions.Interception.Registry; 
using Ninject.Planning; 
using Ninject.Planning.Strategies; 

namespace Common.Interception.Interceptors 
{ 
    /// <summary> 
    /// This is a derivation of InterceptorRegistrationStrategy from Ninject.Extensions.Interception.Planning.Strategies, merged with 
    /// http://stackoverflow.com/questions/6386461/ninject-intercept-any-method-with-certain-attribute 
    /// </summary> 
    public class AlternateInterceptorRegistrationStrategy<TAttribute, TInterceptor> : NinjectComponent, IPlanningStrategy 
     where TAttribute : Attribute 
     where TInterceptor : IInterceptor 
    { 
     protected const BindingFlags DefaultBindingFlags = 
      BindingFlags.Public | 
      BindingFlags.NonPublic | 
      BindingFlags.Instance; 

     public AlternateInterceptorRegistrationStrategy(IAdviceFactory adviceFactory, IAdviceRegistry adviceRegistry, IKernel kernel) 
     { 
      AdviceFactory = adviceFactory; 
      AdviceRegistry = adviceRegistry; 
      Kernel = kernel; 
     } 

     public IKernel Kernel { get; set; } 
     public IAdviceFactory AdviceFactory { get; set; } 
     public IAdviceRegistry AdviceRegistry { get; set; } 

     public virtual void Execute(IPlan plan) 
     { 
      IEnumerable<MethodInfo> candidates = GetCandidateMethods(plan.Type); 

      RegisterClassInterceptors(plan.Type, plan, candidates); 

      foreach (MethodInfo method in candidates) 
      { 
       PropertyInfo property = method.GetPropertyFromMethod(plan.Type); 
       ICustomAttributeProvider provider = (ICustomAttributeProvider)property ?? method; 
       TAttribute[] attributes = provider.GetAllAttributes<TAttribute>(); 

       if (attributes.Length == 0) 
       { 
        continue; 
       } 

       RegisterMethodInterceptor(plan.Type, method); 

       // Indicate that instances of the type should be proxied. 
       if (!plan.Has<ProxyDirective>()) 
       { 
        plan.Add(new ProxyDirective()); 
       } 
      } 
     } 

     protected virtual void RegisterClassInterceptors(Type type, IPlan plan, IEnumerable<MethodInfo> candidates) 
     { 
      var attributes = type.GetAllAttributes<TAttribute>(); 

      if (attributes.Length == 0) 
      { 
       return; 
      } 

      foreach (MethodInfo method in candidates) 
      { 
       PropertyInfo property = method.GetPropertyFromMethod(type); 
       ICustomAttributeProvider provider = (ICustomAttributeProvider) property ?? method; 

       var config = Kernel.Get<IInterceptorConfig>(); 

       if (config.DoNotInterceptAttribute == null) 
       { 
        // A "do not intercept" attribute wasn't defined in the config, so go ahead and register 
        RegisterMethodInterceptor(type, method); 
       } 
       else if (!provider.IsDefined(config.DoNotInterceptAttribute, true)) 
       { 
        // The method wasn't decorated with the "do not intercept" attribute, so go ahead and register 
        RegisterMethodInterceptor(type, method); 
       } 
      } 

      if (!plan.Has<ProxyDirective>()) 
      { 
       plan.Add(new ProxyDirective()); 
      } 
     } 

     protected virtual void RegisterMethodInterceptor(Type type, MethodInfo method) 
     { 
      IAdvice advice = AdviceFactory.Create(method); 

      advice.Callback = request => request.Context.Kernel.Get<TInterceptor>(); 

      var config = Kernel.TryGet<IInterceptorConfig>(); 
      if (config != null) 
      { 
       advice.Order = config.GetOrder<TInterceptor>(); 
      } 

      AdviceRegistry.Register(advice); 
     } 

     protected virtual IEnumerable<MethodInfo> GetCandidateMethods(Type type) 
     { 
      MethodInfo[] methods = type.GetMethods(DefaultBindingFlags); 

      return methods.Where(ShouldIntercept); 
     } 

     protected virtual bool ShouldIntercept(MethodInfo methodInfo) 
     { 
      return methodInfo.DeclaringType != typeof(object) && 
        !methodInfo.IsPrivate;// && 
      //!methodInfo.IsFinal; 
     } 
    } 
} 

IInterceptorConfig.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using Ninject.Extensions.Interception; 

namespace Common.Interception.Interceptors 
{ 
    public interface IInterceptorConfig 
    { 
     IInterceptorConfig SpecifyOrder<TInterceptor>(int order) where TInterceptor : IInterceptor; 

     IInterceptorConfig SpecifyDoNotInterceptAttribute<TAttribute>() where TAttribute : Attribute; 

     int GetOrder<TInterceptor>() where TInterceptor : IInterceptor; 

     Type DoNotInterceptAttribute { get; } 
    } 
} 

InterceptorConfig.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using Ninject.Extensions.Interception; 

namespace Common.Interception.Interceptors 
{ 
    public class InterceptorConfig : IInterceptorConfig 
    { 
     private readonly Dictionary<Type, int> _orderDictionary = new Dictionary<Type, int>(); 

     public IInterceptorConfig SpecifyOrder<TInterceptor>(int order) where TInterceptor : IInterceptor 
     { 
      _orderDictionary.Add(typeof(TInterceptor), order); 
      return this; 
     } 

     public IInterceptorConfig SpecifyDoNotInterceptAttribute<TAttribute>() where TAttribute : Attribute 
     { 
      DoNotInterceptAttribute = typeof(TAttribute); 
      return this; 
     } 

     public int GetOrder<TInterceptor>() where TInterceptor : IInterceptor 
     { 
      return _orderDictionary[typeof(TInterceptor)]; 
     } 

     public Type DoNotInterceptAttribute { get; private set; } 
    } 
} 

TraceInterceptor.cs - 只是一個樣本的攔截

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using Ninject.Extensions.Interception; 

namespace Common.Interception.Interceptors 
{ 
    public class TraceInterceptor : IInterceptor 
    { 
     public void Intercept(IInvocation invocation) 
     { 
      Console.WriteLine("Enter Method"); 

      invocation.Proceed(); 

      Console.WriteLine("Exit Method"); 
     } 
    } 
} 

注:下面是一個簡單的控制檯應用程序,說明了如何爲最高屬性/攔截。這對雙方Ninject.Extensions.Interception.DynamicProxy和Ninject.Extensions.Conventions依賴關係(和所有需要的依賴)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Text; 
using System.Threading.Tasks; 
using Common.Interception.Attributes; 
using Common.Interception.Interceptors; 
using Ninject; 
using Ninject.Extensions.Conventions; 
using Ninject.Planning.Strategies; 
using SomeProject.Infrastructure; 

namespace SomeProject.ConsoleApp 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var kernel = new StandardKernel(); 

      kernel.Components.Add<IPlanningStrategy, AlternateInterceptorRegistrationStrategy<TraceAttribute, TraceInterceptor>>(); 
      kernel.Components.Add<IPlanningStrategy, AlternateInterceptorRegistrationStrategy<AuthorizeAttribute, AuthorizeInterceptor>>(); 

      // I needed a way to specify execution order and "DoNotIntercept" without deriving from attributes that would force ninject references all over my domain 
      // not 100% confident this is the best way - but it works 
      kernel.Bind<IInterceptorConfig>().ToConstant(new InterceptorConfig() 
                  .SpecifyOrder<TraceInterceptor>(1) 
                  .SpecifyOrder<AuthorizeInterceptor>(0) 
                  .SpecifyDoNotInterceptAttribute<DoNotInterceptAttribute>()); 

      // SomeProject.Infrastructure contains my service classes decorated with my custom attributes 
      kernel.Bind(x => x.FromAssembliesMatching("SomeProject.Infrastructure") 
           .SelectAllClasses() 
           .BindToSelf()); 

      var a = kernel.Get<SomeServiceA>(); 
      var b = kernel.Get<SomeServiceB>(); 

      Console.WriteLine("Calling a.DoSomeStuff()..."); 
      a.DoSomeStuff(); 

      Console.WriteLine("Calling b.DoMyThing()..."); 
      b.DoMyThing(); 

      Console.WriteLine("Calling b.NowTraceThis()..."); 
      b.NowTraceThis(); 

      Console.ReadLine(); 
     } 
    } 
} 
+0

像這樣的事情看起來很有希望...... http://www.codeproject.com/Articles/72746/Snap-Simple-NET-Aspect-Oriented-Programming但如果我寧願內置到ninject擴展 – BlakeH 2013-02-27 04:33:03

+0

如果有任何興趣將源代碼作爲github回購,請告訴我!我可以包含一個工作示例。 – BlakeH 2013-02-28 16:06:05

回答

0

練精練後,我發現一個類似的問題已經被問和在這裏找到答案: Ninject Intercept any method with certain attribute?

我將此與Inceptor.Extensions.Interception.Planning.Strategies中的InterceptorRegistrationStrategy類的源代碼結合使用以創建我現在正在使用的派生。

蟋蟀

+1

如果您可以發佈該代碼,我很樂意看到該代碼? – 2013-02-27 20:47:45

+0

當然......我現在在家,但我會盡量讓它在明天發佈。 – BlakeH 2013-02-28 02:56:46

+0

這是怎麼回事?大聲笑 – Brandon 2013-08-21 15:29:28