Note that there are some explanatory texts on larger screens.

plurals
  1. PONinject Conventions and Interception
    text
    copied!<p>I want to decorate my services with attributes for interception, and then have conventions based binding set the interceptors up for me. I don't want my attributes to inherit from the interception attributes... if I can avoid it.</p> <p>For example, I have the following class:</p> <pre><code>[Log] public class SomeClassToLog { public void DoSomething() { ... } } </code></pre> <p>I understand I can bind this as follows:</p> <pre><code>var kernel = new StandardKernel(); kernel.Bind(x =&gt; x.FromAssembliesMatching("SomeProject.*") .SelectAllClasses() .WithAttribute(typeof(LogAttribute)) .BindToSelf().Configure(syntax =&gt; syntax.Intercept().With(LogInterceptor))); </code></pre> <p>How can I do this with different combinations of attributes and interceptors? For example:</p> <p>If I have Log and Authorize attributes I would have to configure 3 sets of bindings? (1 for log without authorize, 1 for authorize without log and one for both log and authorize).</p> <p><strong>Updated:</strong> While I couldn't find a solution based on my original question parameters, I did stumble upon a similar question which lead me to the solution I ended up going with. Here is the source code:</p> <p><strong>Notes:</strong> <em>Common.Interception.Interceptors</em> namespace is in an assembly which has a reference to Ninject.Extensions.Interception (and all of its required dependencies). My attributes are defined in a separate assembly with no dependencies of their own.</p> <p><strong>MiscExtensions.cs</strong></p> <pre><code>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 /// &lt;summary&gt; /// 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 /// &lt;/summary&gt; internal static class MiscExtensions { /// &lt;summary&gt; /// Converts all of the items in the specified series using the specified converter. /// &lt;/summary&gt; /// &lt;typeparam name="TInput"&gt;The type of items contained in the input list.&lt;/typeparam&gt; /// &lt;typeparam name="TOutput"&gt;The type of items to return.&lt;/typeparam&gt; /// &lt;param name="items"&gt;The series of items to convert.&lt;/param&gt; /// &lt;param name="converter"&gt;The converter to use to convert the items.&lt;/param&gt; /// &lt;returns&gt;A list of the converted items.&lt;/returns&gt; public static IEnumerable&lt;TOutput&gt; Convert&lt;TInput, TOutput&gt;(this IEnumerable&lt;TInput&gt; items, Func&lt;TInput, TOutput&gt; converter) { return items.Select(converter); } /// &lt;summary&gt; /// Skips the last items where the count of skipped items is given by count. /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of the enumerable.&lt;/typeparam&gt; /// &lt;param name="source"&gt;The source.&lt;/param&gt; /// &lt;param name="count"&gt;The count of skipped items.&lt;/param&gt; /// &lt;returns&gt;An enumerable that skippes the last items from the source enumerable.&lt;/returns&gt; public static IEnumerable&lt;T&gt; SkipLast&lt;T&gt;(this IEnumerable&lt;T&gt; source, int count) { var enumerator = source.GetEnumerator(); var items = new Queue&lt;T&gt;(); while (enumerator.MoveNext()) { if (count-- &lt;= 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); } /// &lt;summary&gt; /// Gets the types of the parameters of the method. /// &lt;/summary&gt; /// &lt;param name="method"&gt;The method in question.&lt;/param&gt; /// &lt;returns&gt;An array containing the types of the method's parameters.&lt;/returns&gt; public static IEnumerable&lt;Type&gt; GetParameterTypes(this MethodBase method) { return method.GetParameters().Convert(p =&gt; p.ParameterType); } /// &lt;summary&gt; /// Gets the method handle of either the method or its generic type definition, if it is /// a generic method. /// &lt;/summary&gt; /// &lt;param name="method"&gt;The method in question.&lt;/param&gt; /// &lt;returns&gt;The runtime method handle for the method or its generic type definition.&lt;/returns&gt; public static RuntimeMethodHandle GetMethodHandle(this MethodBase method) { var mi = method as MethodInfo; if (mi != null &amp;&amp; mi.IsGenericMethod) { return mi.GetGenericMethodDefinition().MethodHandle; } return method.MethodHandle; } /// &lt;summary&gt; /// Gets the first attribute of a specified type that decorates the member. /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of attribute to search for.&lt;/typeparam&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;returns&gt;The first attribute matching the specified type.&lt;/returns&gt; public static T GetOneAttribute&lt;T&gt;(this ICustomAttributeProvider member) where T : Attribute { var attributes = member.GetCustomAttributes(typeof(T), true) as T[]; return (attributes == null) || (attributes.Length == 0) ? null : attributes[0]; } /// &lt;summary&gt; /// Gets the first attribute of a specified type that decorates the member. /// &lt;/summary&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;param name="type"&gt;The type of attribute to search for.&lt;/param&gt; /// &lt;returns&gt;The first attribute matching the specified type.&lt;/returns&gt; public static object GetOneAttribute(this ICustomAttributeProvider member, Type type) { object[] attributes = member.GetCustomAttributes(type, true); return (attributes == null) || (attributes.Length == 0) ? null : attributes[0]; } /// &lt;summary&gt; /// Gets an array of attributes matching the specified type that decorate the member. /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of attribute to search for.&lt;/typeparam&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;returns&gt;An array of attributes matching the specified type.&lt;/returns&gt; public static T[] GetAllAttributes&lt;T&gt;(this ICustomAttributeProvider member) where T : Attribute { return member.GetCustomAttributes(typeof(T), true) as T[]; } /// &lt;summary&gt; /// Gets an array of attributes matching the specified type that decorate the member. /// &lt;/summary&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;param name="type"&gt;The type of attribute to search for.&lt;/param&gt; /// &lt;returns&gt;An array of attributes matching the specified type.&lt;/returns&gt; public static object[] GetAllAttributes(this ICustomAttributeProvider member, Type type) { return member.GetCustomAttributes(type, true); } /// &lt;summary&gt; /// Determines whether the member is decorated with one or more attributes of the specified type. /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of attribute to search for.&lt;/typeparam&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;returns&gt;&lt;see langword="True"/&gt; if the member is decorated with one or more attributes of the type, otherwise &lt;see langword="false"/&gt;.&lt;/returns&gt; public static bool HasAttribute&lt;T&gt;(this ICustomAttributeProvider member) where T : Attribute { return member.IsDefined(typeof(T), true); } /// &lt;summary&gt; /// Determines whether the member is decorated with one or more attributes of the specified type. /// &lt;/summary&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;param name="type"&gt;The type of attribute to search for.&lt;/param&gt; /// &lt;returns&gt;&lt;see langword="True"/&gt; if the member is decorated with one or more attributes of the type, otherwise &lt;see langword="false"/&gt;.&lt;/returns&gt; public static bool HasAttribute(this ICustomAttributeProvider member, Type type) { return member.IsDefined(type, true); } /// &lt;summary&gt; /// Determines whether the member is decorated with an attribute that matches the one provided. /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of attribute to search for.&lt;/typeparam&gt; /// &lt;param name="member"&gt;The member to examine.&lt;/param&gt; /// &lt;param name="attributeToMatch"&gt;The attribute to match against.&lt;/param&gt; /// &lt;returns&gt;&lt;see langword="True"/&gt; if the member is decorated with a matching attribute, otherwise &lt;see langword="false"/&gt;.&lt;/returns&gt; public static bool HasMatchingAttribute&lt;T&gt;(this ICustomAttributeProvider member, T attributeToMatch) where T : Attribute { T[] attributes = member.GetAllAttributes&lt;T&gt;(); if ((attributes == null) || (attributes.Length == 0)) { return false; } return attributes.Any(attribute =&gt; attribute.Match(attributeToMatch)); } } } </code></pre> <p><strong>AlternateInterceptorRegistrationStrategy.cs</strong></p> <pre><code>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 { /// &lt;summary&gt; /// 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 /// &lt;/summary&gt; public class AlternateInterceptorRegistrationStrategy&lt;TAttribute, TInterceptor&gt; : 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&lt;MethodInfo&gt; 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&lt;TAttribute&gt;(); if (attributes.Length == 0) { continue; } RegisterMethodInterceptor(plan.Type, method); // Indicate that instances of the type should be proxied. if (!plan.Has&lt;ProxyDirective&gt;()) { plan.Add(new ProxyDirective()); } } } protected virtual void RegisterClassInterceptors(Type type, IPlan plan, IEnumerable&lt;MethodInfo&gt; candidates) { var attributes = type.GetAllAttributes&lt;TAttribute&gt;(); if (attributes.Length == 0) { return; } foreach (MethodInfo method in candidates) { PropertyInfo property = method.GetPropertyFromMethod(type); ICustomAttributeProvider provider = (ICustomAttributeProvider) property ?? method; var config = Kernel.Get&lt;IInterceptorConfig&gt;(); 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&lt;ProxyDirective&gt;()) { plan.Add(new ProxyDirective()); } } protected virtual void RegisterMethodInterceptor(Type type, MethodInfo method) { IAdvice advice = AdviceFactory.Create(method); advice.Callback = request =&gt; request.Context.Kernel.Get&lt;TInterceptor&gt;(); var config = Kernel.TryGet&lt;IInterceptorConfig&gt;(); if (config != null) { advice.Order = config.GetOrder&lt;TInterceptor&gt;(); } AdviceRegistry.Register(advice); } protected virtual IEnumerable&lt;MethodInfo&gt; GetCandidateMethods(Type type) { MethodInfo[] methods = type.GetMethods(DefaultBindingFlags); return methods.Where(ShouldIntercept); } protected virtual bool ShouldIntercept(MethodInfo methodInfo) { return methodInfo.DeclaringType != typeof(object) &amp;&amp; !methodInfo.IsPrivate;// &amp;&amp; //!methodInfo.IsFinal; } } } </code></pre> <p><strong>IInterceptorConfig.cs</strong></p> <pre><code>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&lt;TInterceptor&gt;(int order) where TInterceptor : IInterceptor; IInterceptorConfig SpecifyDoNotInterceptAttribute&lt;TAttribute&gt;() where TAttribute : Attribute; int GetOrder&lt;TInterceptor&gt;() where TInterceptor : IInterceptor; Type DoNotInterceptAttribute { get; } } } </code></pre> <p><strong>InterceptorConfig.cs</strong></p> <pre><code>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&lt;Type, int&gt; _orderDictionary = new Dictionary&lt;Type, int&gt;(); public IInterceptorConfig SpecifyOrder&lt;TInterceptor&gt;(int order) where TInterceptor : IInterceptor { _orderDictionary.Add(typeof(TInterceptor), order); return this; } public IInterceptorConfig SpecifyDoNotInterceptAttribute&lt;TAttribute&gt;() where TAttribute : Attribute { DoNotInterceptAttribute = typeof(TAttribute); return this; } public int GetOrder&lt;TInterceptor&gt;() where TInterceptor : IInterceptor { return _orderDictionary[typeof(TInterceptor)]; } public Type DoNotInterceptAttribute { get; private set; } } } </code></pre> <p><strong>TraceInterceptor.cs</strong> - just a sample interceptor</p> <pre><code>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"); } } } </code></pre> <p><strong>Notes:</strong> Here is a simple console app that shows how to wire up the attributes/interceptors. This has dependencies on both Ninject.Extensions.Interception.DynamicProxy and Ninject.Extensions.Conventions (and all their required dependencies)</p> <pre><code>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&lt;IPlanningStrategy, AlternateInterceptorRegistrationStrategy&lt;TraceAttribute, TraceInterceptor&gt;&gt;(); kernel.Components.Add&lt;IPlanningStrategy, AlternateInterceptorRegistrationStrategy&lt;AuthorizeAttribute, AuthorizeInterceptor&gt;&gt;(); // 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&lt;IInterceptorConfig&gt;().ToConstant(new InterceptorConfig() .SpecifyOrder&lt;TraceInterceptor&gt;(1) .SpecifyOrder&lt;AuthorizeInterceptor&gt;(0) .SpecifyDoNotInterceptAttribute&lt;DoNotInterceptAttribute&gt;()); // SomeProject.Infrastructure contains my service classes decorated with my custom attributes kernel.Bind(x =&gt; x.FromAssembliesMatching("SomeProject.Infrastructure") .SelectAllClasses() .BindToSelf()); var a = kernel.Get&lt;SomeServiceA&gt;(); var b = kernel.Get&lt;SomeServiceB&gt;(); Console.WriteLine("Calling a.DoSomeStuff()..."); a.DoSomeStuff(); Console.WriteLine("Calling b.DoMyThing()..."); b.DoMyThing(); Console.WriteLine("Calling b.NowTraceThis()..."); b.NowTraceThis(); Console.ReadLine(); } } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload