2013-02-26 7 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))); 

Как я могу сделать это с помощью различных комбинаций атрибутов и перехватчики? Например:

Если у меня есть атрибуты Log и Authorize, мне нужно было бы настроить 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

+0

Если есть какой-либо интерес к получению исходного кода в качестве рефинга github, дайте мне знать! Я могу включить рабочий образец. – BlakeH

ответ

0

После обезжиривания и размыва, я обнаружил, что подобный вопрос был задан и ответил здесь: Ninject Intercept any method with certain attribute?

Я использовал это в сочетании с исходным кодом класса InterceptorRegistrationStrategy в Ninject.Extensions.Interception.Planning.Strategies, чтобы создать деривацию, которую я сейчас использую.

сверчки

+1

Мне было бы интересно увидеть этот код, если бы вы могли его опубликовать? –

+0

Уверенная вещь ... Я сейчас дома, но я постараюсь ее опубликовать завтра. – BlakeH

+0

Что случилось с этим? лол – Brandon