Click here to Skip to main content
13,202,706 members (55,340 online)
Click here to Skip to main content
Add your own
alternative version

Stats

15.5K views
229 downloads
18 bookmarked
Posted 27 Nov 2015

Cross cutting concerns in .Net applications

, 27 Nov 2015
Rate this:
Please Sign up or sign in to vote.
How to use Microsoft Unity Interception as a solution for cross cutting concerns in a .NET application

 

CrossIntroduction

Cross cutting concerns are common methods provided by a specific service used by all layers in an application; “cutting through the whole application”.
Examples are:
Logging
Caching
Authorisation
Error handling
Auditing 

This article describes different examples how to implement cross cutting concerns in a .Net application.
With every example the pros and cons are listed. 
The conclusion is made that using Microsoft Unity for implementing cross cutting concerns is probably the most suitable manner regarding flexibility, maintenance, amount of work and applying to SOLID software design principles.

Examples

Adding code to consuming classes

When cross cutting concerns are implemented by just simply adding code in consuming classes, the code will be polluted with dependencies that will be a burden when the code is under test or parallel development is done by the development team.

An example is:

public class ProductRepository    
{
 public void Update(string productName)
 {
  Update(productName);
  new EventLog("MyLog").WriteEntry(string.Format("{0} is updated in the database"));
 }
}

The above code works fine but there are some disadvantages. 
It violates the single responsibility principle with both an update and a logging functionality.
The update method can’t be tested in an isolated manner because the update method can’t be called without triggering the logging service.
The logger (EventLog class) can’t be substituted with a stub or fake logger in an easy way.
The ProductRepository is dragging along a dependency of the logger dll which means that whenever there is something wrong with the logger codebase, there is something wrong with the ProductRepository. 

Using dependency injection

Using dependency injection is a better solution but still it needs to be injected in every class that wants to consume the concern and can therefore be quit repetitive.
Also the interface presents now a nonspecific behaviour of the class, e.g. the logging is a non-intrinsic behaviour of the ProductRepository but it is (too) obtrusive present in the interface.

public class ProductRepository
{
        ILogger _logger;
 
        public ProductRepository(ILogger logger)
        {
            _logger = logger;
        }
 
        public void Update(string productName)
        {
            Update(productName);
            logger.WriteEntry(string.Format("{0} is updated in the database"));
        }
}

Using Ambient Context

Ambient Context is represented by a defined set of properties or methods that can be consumed as if they are application variables (or global variables).

One way of realizing this is to make use of static properties or static methods with a writable singleton class.
The ambient context is best to be used when the public methods in the context return a value that is used by the consumer. 
When the public methods return void e.g. the methods are writing to a log, cache etc., is has no use in “polluting” the original code when no return value is used by the original class.
Using interception is a better solution in that case because no return value has to be passed to the consumer.
The next example shows the  abstract class TimeProvider 

public abstract class TimeProvider
    {
        private static TimeProvider current;
 
        static TimeProvider()
        {
            TimeProvider.current = new DefaultTimeProvider();
        }
 
        public static TimeProvider Current
        {
            get { return TimeProvider.current; }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                TimeProvider.current = value;
            }
        }
 
        public abstract DateTime UtcNow { get; }
 
        public static void Reset()
        {
            TimeProvider.current = new DefaultTimeProvider();
        }
    }
 
 
    public class DefaultTimeProvider : TimeProvider
    {
        public override DateTime UtcNow
        {
            get { return DateTime.UtcNow; }
        }
    }

Good usage of the ambient context:
Make sure it has an intrinsic default, it should always work without assigning a custom context.
The context can’t be nullable, a consumer should be able to always query the ambient context.
This can be arranged with a guard clause that checks if the setter value is not null.

Advantages of ambient context:
Always available
The timeprovider class can be used everywhere in the application, it is always available without expanding the interfaces/api’s (obeying the interface segregation principle)
Mockable
The timeprovider class is mockable with the use of a mocking framework (NSubstitute in the example following)

var timeProviderStub = Substitute.For<TimeProvider>();
timeProviderStub.UtcNow.Returns(DateTime.UtcNow.AddYears(-1));
Assert.AreEqual(DateTime.UtcNow.AddYears(-1).Year, timeProviderStub.UtcNow.Year);

Disadvantages of ambient context:

Black boxing
The consumer of a TimeProvider is (more) Black boxed. 
It is not clear at first sight (via its interface) that a consumer is making use of the TimeProvider to get the time.
If the TimeProvider is changed in behaviour, all consumers will change with that and the direct source of change can be hard to find.
Tricky
As with every application wide scope instance/variable, it is possible when not implemented correctly that the value or reference can be changed due for example to different parallel threat complications. 

Decorator

The decorator pattern is one of the most obvious ways of complying with SOLID programming principles. A good implementation will demonstrate all principles.
A decorator acts as a wrapper around a class by means of consuming that class and simultaneously implementing the same interface of that class. 
This makes it possible to substitute the original class and to override the methods in that class.
The overrides can delegate the call to the injected class plus add extra functionality of its own like for example logging or caching. 

Code example:

public interface IProductRepository
{
   void Update(string productname);
}

public class ProductRepository : IProductRepository
{
  public void Update(string productname)
  {
    //write to database
  }
}

public class ProductRepositoryWithLog : IProductRepository
    {
        private IProductRepository _productRepository;
 
        public ProductRepositoryWithLog(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
 
        public void Update(string productName)
        {
            //Delegate to original update method
            _productRepository.Update(productName);
            //Decoration with logging feature
            new EventLog("MyLog").WriteEntry(string.Format("{0} is updated in the database"));
        }
    }

Advantage of using a decorator
Applying to SOLID principles with all the conveyed advantages : more readable, testable, extensible, maintainable and loosely coupled code.
Disadvantage:
For larger projects it can be verbose and repetitive which violates the DRY principle.

Aspect Oriented Programming

AOP is mechanism to automatically wire up decorators to the original classes without the need for writing the actual decorators. The cross cutting concerns functionality presented as aspects are attached to the original class with the means of custom attributes.
PostSharp is such an AOP framework that uses code interweaving at compile time to intercept and decorates the original class with the cross cutting concerns aspects during the compile process.
There are pro’s and con’s to use an AOP framework like PostSharp and it all depends on the project and situation.

One of the disadvantage is that most AOP mechanisms use attributes to declare aspects.
Aspects are the interleaved pieces of code that an AOP frameworks adds in the post compilation steps. This can lead to debugging problems and the so-called Vendor Lock-In anti-pattern. Also using attributes will make your code less loosely coupled by creating a hard dependency between your code and the AOP framework in use.

Interception with Unity

Interception is a design pattern used to alter or amend code in a transparent, non-obtrusive way.
With (Microsoft) Unity it is possible to use (dynamic) interception during run-time without altering the compiled code. 
Unity provides a way to define a decorator that can be used by an arbitrary class without the requirement to implement this decorator as an interface by the class.
There are in short two types of interception possible with Unity: Instance interception and Type interception.

Interception Type : Instance

Unity creates a proxy object based on the interface the target object implements.
To implement instance interception with Unity, two interceptor types are provided by Microsoft: 
InterfaceInterceptor
An instance interceptor that works by generating a proxy class on the fly for a single interface.

TransparentProxyInterceptor
An instance interceptor that uses remoting proxies to do the interception
The advantage of the this interceptor is that it can intercept multiple interfaces but the slower speed of this interceptor is the disadvantage

Implementation

Create a class that implements: IinterceptionBehavior
This class will act as the decorator
The Invoke method is called when a member is intercepted.
In the Invoke method define what cross cutting concern you need e.g. logging:

public class LoggingInterceptionBehavior : IInterceptionBehavior
    {
        public IMethodReturn Invoke(IMethodInvocation input,
        GetNextInterceptionBehaviorDelegate getNext)
        {
            // Before invoking the method on the original target.
            WriteToLog(String.Format(
            "Invoking method {0} at {1}",
            input.MethodBase, DateTime.Now.ToLongTimeString()));
            // Invoke the next behavior in the chain.
            var result = getNext()(input, getNext);
            // After invoking the method on the original target.
 
            if (result.Exception != null)
            {
                WriteToLog(String.Format(
                "Method {0} threw exception {1} at {2}",
                input.MethodBase, result.Exception.Message,
                DateTime.Now.ToLongTimeString()));
            }
 
            else
            {
                WriteToLog(String.Format(
                "Method {0} returned {1} at {2}",
                input.MethodBase, result.ReturnValue,
                DateTime.Now.ToLongTimeString()));
            }
            return result;
        }
 
        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }
 
        public bool WillExecute
        {
            get { return true; }
        }
 
        private void WriteToLog(string message)
        {
            EventLog eventLog = new EventLog("");
            eventLog.Source = "Interception";
            eventLog.WriteEntry(string.Format("{0}", message, EventLogEntryType.Information));
        }
    }

Next define with the container the class and interface that you want to for interception (the class you normally would write a decorator for) and with that statement you define

the type of interception and which decorator (e.g. LoggingInterceptionBehavior)

container.AddNewExtension<Interception>();
//class to decorate
container.RegisterType<IProductRepository, ProductRepository>(
//interceptor type
new Interceptor<InterfaceInterceptor>(),
//decorator
new InterceptionBehavior<LoggingInterceptionBehavior>());

Interception Type : Type

Unity creates an object based on the type of the target object, it is actually a subtype of the target object. The interceptor used is called a VirtualMethodInterceptor

VirtualMethodInterceptor

Intercept all methods including internals
Can only use when creating an object, not possible on existing instances
For example, you cannot use type interception on a WCF service proxy object that is created for you by a channel factory

Disadvantage of these interception types is there is no granularity in what you intercept of the class.

Implementation

Create a class that implements: IinterceptionBehavior.
 

Next register the container the same as with the interface interception with the difference that you use the VirtualMethodInterceptor:

container.AddNewExtension<Interception>();
container.RegisterType<ProductRepository>(
new Interceptor<VirtualMethodInterceptor>(),
new InterceptionBehavior<LoggingInterceptionBehavior>());

 

Overview
The following figure shows the advantages and disadvantages of the two types of interception 

Interception Type

Advantages

Disadvantages

(instance)

Transparent Proxy Interceptor

Can intercept all methods of the target object (virtual, non-virtual, or interface).

The object must either implement an interface or inherit from System.MarshalByRefObject. If the marshal by reference object is not a base class, you can only proxy interface methods. The Transparent Proxy process is much slower than a regular method call.

(instance)

Interface Interceptor

Allows interception on any object that implements the target interface. It is much faster than the TransparentProxyInterceptor.

It only intercepts methods on a single interface. It cannot cast a proxy back to the target object's class or to other interfaces on the target object.

(type)

Virtual Method Interceptor

Calls are much faster than the Transparent Proxy Interceptor.

Interception only happens on virtual methods. You must set up interception at object creation time and cannot intercept an existing object.

Unity configuration

There are different ways to configure Unity.
You can use policy and attributes.

Policy Injection

Policy injection is an interceptor that captures calls to objects you resolve through the (unity) container, and applies a policy that uses call handlers and matching rules inherited from Unity to define its policy injection behaviour on a per-method basis.

The call handler is the actual decorator and behaviour rules defined in the application configuration file describe how and when the decorator is used.
Policy injection gives you more control over what types of classes or methods (members) are interception by means of behaviour rules. Policy injection is not bounded to a specific class or interface, the behaviour rules describe in a declarative way what members are intercepted.

Matching examples:

Matching rule

Description

Assembly Matching Rule

Selects classes in a specified assembly.

Custom Attribute Matching Rule

Selects classes or members that have an arbitrary attribute applied.

Member Name Matching Rule

Selects class members based on the member name.

Method Signature Matching Rule

Selects class methods that have a specific signature.

Namespace Matching Rule

Selects classes based on the namespace name.

Parameter Type Matching Rule

Selects class members based on the type name of a parameter for a member of the target object.

Property Matching Rule

Selects class properties by name and accessor type.

Return Type Matching Rule

Selects class members that return an object of the specified type.

Tag Attribute Matching Rule

Selects class members that carry a Tag attribute with the specified name.

Type Matching Rule

Selects classes that are of a specified type.

Implementation

Create a class that implements IcallHandler
This class will act as the decorator.
A call handler can either be written very specifically for a particular method, or very generally, to handle any member.
The Invoke method is called when a member is intercepted.
In the Invoke method define what cross cutting concern you need e.g. logging:

public class LoggingCallHandler : ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, 
    GetNextHandlerDelegate getNext)
        {
            System.Diagnostics.Debug.WriteLine("Begin");
 
            string message =
                string.Format(
               "Assembly : {0}.{1}Location : {2}.{3}Calling Class : {4}",
                input.MethodBase.DeclaringType.Assembly.FullName,
                Environment.NewLine,
                input.MethodBase.DeclaringType.Assembly.Location,
                Environment.NewLine,
                input.MethodBase.DeclaringType.FullName
                );
 
            WriteToLog(string.Format("{0} is called with parameters '{1}'", 
                input.MethodBase.Name,
                input.Inputs[0]));
 
            WriteToLog(message);
            
            IMethodReturn result = getNext().Invoke(input, getNext);
            WriteToLog(string.Format("{0} is ended", input.MethodBase.Name));
            System.Diagnostics.Debug.WriteLine("End");
            return result;
        }
 
        // order in which handler will be invoked
        public int Order { get; set; }
 
        private void WriteToLog(string message)
        {
            EventLog eventLog = new EventLog("");
            eventLog.Source = "Interception";
            eventLog.WriteEntry(string.Format("{0}", 
        message,EventLogEntryType.Information));
        }
    }

Next you only need to describe the policy in the application configuration file and load this behaviour in the container.

E.g.:

<container name="Demo">
      <extension type="Interception"/>
      <register type="Logger.CallHandler.LoggingCallHandler, Logger"/>
      <register type="DynamicInterception.IProductRepository, DynamicInterception" 

       mapTo="DynamicInterception.ProductRepository, DynamicInterception"/>
      <register type="DynamicInterception.ProductRepository, DynamicInterception">
        <interceptor type="VirtualMethodInterceptor"/>
        <interceptionBehavior type="PolicyInjectionBehavior"/>
      </register>
      <interception>
        <policy name="LogPolicy">
 
       <matchingRule type="NamespaceMatchingRule" name=" NameSpaceRule">
            <constructor>
              <param name="namespaceName" value="DynamicInterception"/>
            </constructor>
          </matchingRule>
         
          
       <matchingRule type="MemberNameMatchingRule" name="MemberMatch">
            <constructor>
              <param name="nameToMatch" value="Update" />
            </constructor>
          </matchingRule>
          
          <callHandler type="Logger.CallHandler.LoggingCallHandler, Logger" name="logging handler"/>
        </policy>
      </interception>
   </container>

Load these rules in the container:

container.LoadConfiguration("Demo");

The result will be that every method with the name “update” in the namespace DynamicInterception will be intercepted and thus have a logging feature.

Policy Injection and Attributes

With using attributes there is no need to configure any policies, but you do need to define your attributes. The attributes declare if a member must be intercepted.
Important is that when using attributes SOLID rules are disobeyed,  attributes forces you to make a reference to the dll that services the cross cutting concern.

If you apply attributes to methods you wish to intercept, you’re effectively creating a hard dependency on the aspect you’ve applied. Let’s say that you want to handle security on a certain set of methods. 
The moment you declare a security-attribute on your methods you’re creating a hard dependency on that security implementation. 

Implementation

First register the policy injection with attributes in the container:

container.AddNewExtension<Interception>();
container.RegisterType<IProductRepository, ProductRepository>(
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<PolicyInjectionBehavior>());

Next create an attribute that implements the: HandlerAttribute
The attribute AttributeUsage specifies the language elements to which the attribute can be applied.

e.g. 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method)]
    public class LogCallBeginEndAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return container.Resolve<LoggingCallHandler>();
        }
    }

Use the attribute on members you want to intercept :
[LogCallBeginEndAttribute()]
public virtual void Delete(string productname)
{
   //delete in database
}

 

Points of Interest

Required tooling

In order to use Interception with Unity the following requirements must be met:
Operating system: Microsoft Windows® 7 Professional, Enterprise or Ultimate; Windows Server 2003 R2; Windows Server 2008 with Service Pack 2; Windows Server 2008 R2; Windows Vista with Service Pack 2; or Windows XP with Service Pack 3.
Microsoft .NET Framework 3.5 with Service Pack 1 or Microsoft .NET Framework 4.0. or higher
Unity Application Block for .Net  (available at nuget.org)
Unity Interception Extension for .Net (available at nuget.org)

Dependencies

There are no dependencies except the use of the required tooling.

Guidelines

Best practice is to use Policy Injection to avoid the use of filtering in classes that implement the IinterceptionBehavior interface.
When using Policy Injection start with narrow rules to avoid that all members in the project are intercepted.

History

First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Christian Vos
Founder Rood Mitek
Netherlands Netherlands
Christian Vos .MCSD has been an independent Microsoft developer since 1999 and is specialized in distributed Microsoft .Net platform based Web applications. He is founder of the company Rood Mitek which has developed applications for companies like Philips Electronics, ASML, Lucent Technologies, Amoco, AT&T etc.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionComparison of the above technique with PostSharp Pin
mbuthiagrg30-Nov-15 6:48
membermbuthiagrg30-Nov-15 6:48 
AnswerRe: Comparison of the above technique with PostSharp Pin
anujk029230-Nov-15 21:28
memberanujk029230-Nov-15 21:28 
AnswerRe: Comparison of the above technique with PostSharp Pin
Christian Vos30-Nov-15 22:08
memberChristian Vos30-Nov-15 22:08 
GeneralRe: Comparison of the above technique with PostSharp Pin
Grgmn1-Dec-15 0:08
memberGrgmn1-Dec-15 0:08 
GeneralRe: Comparison of the above technique with PostSharp Pin
Christian Vos3-Dec-15 19:54
memberChristian Vos3-Dec-15 19:54 
GeneralRe: Comparison of the above technique with PostSharp Pin
Grgmn15-Dec-15 5:56
memberGrgmn15-Dec-15 5:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171020.1 | Last Updated 27 Nov 2015
Article Copyright 2015 by Christian Vos
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid