Click here to Skip to main content
15,881,381 members
Articles / Programming Languages / C#

Cross-cutting concerns via Interception on Windows Phone

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
2 Aug 2012CPOL8 min read 22.3K   4   6
The article describes how to bring the Aspect-Oriented programming practices to the WP7 platform
  • Introduction
    • Example
    • What is wrong
  • Interception
  • Dependency Injection Container
  • Windows Phone Implementation
    • Proxy class
    • Proxy mapping
    • Proxy auto generation
    • Result
  • How to use: SecureBox project
    • Set up PhoneCore Framework
  • Summary
  • Links
  • Useful books
  • History

Introduction

At the beginning, I'd like to show example which I guess is the best way to introduce the main idea of the article.

Example

For example, you have the requirement to implement a simple calculator which supports only two simple operations: addition and multiplication, of numbers. Here is a simple implementation:

C#
public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Mul(int a, int b)
    {
        return a*b;
    }
} 

It works great and it is clear what the code does: just applies operation to two numbers. However, if you call Add/Mul methods this way:

C#
calc.Add(Int32.MaxValue, 100);
calc.Mul(Int32.MaxValue, 100) ; 

you will receive negative integer. Unfortunately the result isn't acceptable for service consumer. This can be fixed by checked keyword:

C#
 public class Calculator
{
    public int Add(int a, int b)
    {
        checked
        {
            return a + b;
        }
    }
    public int Mul(int a, int b)
    {
        checked
        {
            return a*b;
        }
    }
} 

Great! But one day, there is a bug on production environment related to the service consumer and you want to trace all method invocations:

C#
public class Calculator
{
    public int Add(int a, int b)
    {
        Log.Write("Calculator.Add: a={0}, b={1}", a, b);
        checked
        {
            return a + b;
        }
    }
    public int Mul(int a, int b)
    {
        Log.Write("Calculator.Mul: a={0}, b={1}", a, b);
        checked
        {
            return a*b;
        }
    }
} 

Fine! Afterwards, the consumer of your service say that your calculator is too slow on production environment. The explanation that the manipulation of numbers is very fast doesn't work. Even worse, you can't use a profiler tool to attach on production environment, so you decide to measure method execution time:

C#
public class Calculator
{
    Stopwatch watch = new Stopwatch();
    public int Add(int a, int b)
    {
        Log.Write("Calculator.Add: a={0}, b={1}", a, b);
        watch.Restart();
        checked
        {
            var c = a + b;
        }
        watch.Stop();
        Log.Write("Calculator.Add: {0} elapsed ms", watch.ElapsedMilliseconds);
        return c;
    }
    public int Mul(int a, int b)
    {
        Log.Write("Calculator.Mul: a={0}, b={1}", a, b);
        watch.Restart();
        checked
        {
            var c = a * b;
        }
        watch.Stop();
        Log.Write("Calculator.Mul: {0} elapsed ms", watch.ElapsedMilliseconds);
        return c;
    }
} 

At the moment, you notice that something is going wrong in comparison to the first version and you decide to apply refactoring:

C#
public class Calculator
{
    Stopwatch watch = new Stopwatch();
    public int Add(int a, int b)
    {
        return Execute("Add", () => { checked { return a + b; } });
    }

    public int Mul(int a, int b)
    {
        return Execute("Mul", () => { checked { return a * b; } });
    }

    private int Execute(string methodName, Func<int> expression)
    {
        Log.Write("{0}: a={1}, b={2}", methodName, a, b);
        watch.Restart();
        int c;
        c = expression.Invoke();
        watch.Stop();
        Log.Write("{0}: {1} elapsed ms",methodName, watch.ElapsedMilliseconds);
        return c;
    }
} 

What is wrong?

This example can be extended by new requirement, but I think it is time to look at the immediate result. The code works perfect and provides the following features:

  • Logging
  • Error handling
  • Profiling

However, one day you need to implement new service which supports all these features and new ones. What to do? Extract this functionality using Helper class? Seems like the answer, but it isn't. Your class has already dependency on logging subsystem, profiling methods, additional internal behavior, etc. This is wrong from the following reasons:

  • Code reuse - the dependencies make the code reuse process more difficult
  • Violate Single Responsibility Principle - a class should only have a single responsibility which should do one thing and do it well
  • It isn't clear what the code actually does - follows from the previous reason

Actually, the logging, profiling, etc. are the features related to the "cross-cutting concern" term:

"In computer science, cross-cutting concerns are aspects of a program which affect other concerns. These concerns often cannot be cleanly decomposed from the rest of the system in both the design and implementation, and can result in either scattering (code duplication), tangling (significant dependencies between systems), or both" [1]

This term is essential for understanding of Aspect-Oriented programming [2]. Concerning to the example above, even we extract the logging, profiling aspects, we still need use them without explicit dependencies. It can be achieved by AOP technic - Interception.

Interception

The concept of interception is simple: we wish to be able to intercept the call between a consumer and a service and execute some code before or after service invocation:

334685/Interception.png

The typical implementation of the interception is Decorator pattern of GoF. It describes how to "attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality" (1). Nevertheless, this approach requires you to return to the service consumer the instance of decorator class, not the real object. Good point to have the tool which helps you to compose the object graph, manage dependencies between objects and decorates them if it is necessary. It can be achieved by Dependency Injection container.

Dependency Injection Container

Dependency Injection container is a big topic and I'm not going to dive deeper. I may just recommend the book "Dependency Injection in .NET" by Mark Seemann (2). Shortly, DI container provides the following features:

  • Manage object composition
  • Interception
  • Lifetime management

There are a lot of well-know DI containers:

  • Unity
  • Castle Winsdor
  • Spring.NET
  • StructureMap

However, if you are programming for Windows Phone platform your list is shorter. So, the example below uses custom implementation of DI container of PhoneCore Framework 0.6.3 and shows how to intercept method invocation using compile-time generated proxy classes.

Windows Phone Implementation

This part describes the main idea of the article: intercept method invocation on windows phone using PhoneCore Framework. First, there is no magic as WP7 doesn't allow you to load dynamically assemblies which aren't part of deployment package (xap-file). Therefore, there is no sense to create dynamic proxy classes using Reflection API or Mono.Cecil because you can't load it in AppDomain. There is only one way to implement interception in terms of AOP: compile-time weaving. It means that dynamic proxy should be generated at compile time and deployed as the part of the deployment xap-file. When client calls Resolve methods of DI container (explicitly or implicitly via property injection using DependencyAttribute in current implementaion of PhoneCore Framework), the proxy class is returned instead. It contains the special logic in each interface methods that runs custom behaviors (aspects):

334685/Pipeline.png

There are the restrictions in current implementation of PhoneCore Frameworks' interception engine:

  • The concrete type should be registered in container, e.g.: Container.RegisterType<IClass,Class>()
  • The type should be non-generic (but generic methods are supported)
  • The concrete type should be consumed using container explicit or implicit, e.g. Container.Resolve<IClass>()
  • Special Decorator (Proxy) class should be already generated at compile time, mapped to interface type and deployed to xap package.

Proxy class

The good illustration of concept is SecureBox project. Let's look at example of magic proxy of SettingsService class:

C#
using PhoneCore.Framework.IoC.Interception.Proxies;
namespace PhoneCore.Framework.Storage
{
    public class SettingServiceProxy : ProxyBase, PhoneCore.Framework.Storage.ISettingService
    {
        public void Save(System.String key, System.Object value)
        {
            var methodInvocation = BuildMethodInvocation(MethodBase.GetCurrentMethod(), key, value);
            RunBehaviors(methodInvocation);
        }

        public System.Boolean IsExist(System.String key)
        {
            var methodInvocation = BuildMethodInvocation(MethodBase.GetCurrentMethod(), key);
            return RunBehaviors(methodInvocation).GetReturnValue<System.Boolean>();
        }

        public T Load<T>(System.String key)
        {
            var methodInvocation = BuildMethodInvocation(MethodBase.GetCurrentMethod(), key);
            methodInvocation.GenericTypes.Add(typeof(T));
            return RunBehaviors(methodInvocation).GetReturnValue<T>();
        }

    }
} 

As you can see, it is inherited from ProxyBase class and implements ISettingsService. It is already included into solution as cs file. The ProxyBase class exposes some helper methods which build method invocation context and run the chain of additional behaviors (aspects), e.g. logging, profiling, validation. The invocation context stores the actual parameters and method signature which can be used by behaviors:

C#
namespace PhoneCore.Framework.IoC.Interception.Behaviors
{
    /// <summary>
    /// Executes and measures execution time
    /// </summary>
    public class ProfileBehavior: ExecuteBehavior
    {
      ...
        public override IMethodReturn Invoke(MethodInvocation methodInvocation)
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            var result = base.Invoke(methodInvocation);
            watch.Stop();
            __trace.Info(__category, string.Format("{0}.{1} execution time: {2} ms", 
                methodInvocation.Target.GetType(), methodInvocation.MethodBase.Name, watch.ElapsedMilliseconds));
            return result;
        }
    }
} 

All behaviors are implementing the simple interface:

C#
using System;
using PhoneCore.Framework.Configuration;

namespace PhoneCore.Framework.IoC.Interception.Behaviors
{
    /// <summary>
    /// Represents an additional behavior of method invocation
    /// </summary>
    public interface IBehavior: IConfigurable
    {
        /// <summary>
        /// The name of behavior
        /// </summary>
        string Name { get; set; }

        /// <summary>
        /// Provides the way to attach additional behavior to method
        /// </summary>
        /// <param name="methodInvocation"></param>
        /// <returns></returns>
        IMethodReturn Invoke(MethodInvocation methodInvocation);
    }
} 

So, this is the place for implementation of logging, profiling, etc.

Proxy Mapping

You can map proxies to interfaces using two ways:

• Configuration:

XML
<interception>
<!-- default behaviors -->
    <behaviors />       
    <components>
      <component interface="PhoneCore.Framework.UnitTests.Stubs.Container.IClassA,PhoneCore.Framework.UnitTests"
             proxy="PhoneCore.Framework.UnitTests.Stubs.Container.ClassAProxy,PhoneCore.Framework.UnitTests"
             name ="ClassAProxy">
        <behaviors>
          <clear />
          <behavior name="execute" type="PhoneCore.Framework.IoC.Interception.Behaviors.ExecuteBehavior,PhoneCore.Framework" />
          <behavior name="trace" type="PhoneCore.Framework.IoC.Interception.Behaviors.TraceBehavior,PhoneCore.Framework" />
        </behaviors>
      </component>
... 

• Programmatically:

C#
Container.Register(Component
    .For<IMyClass>()
    .Use<MyClass>().
    Proxy<MyClassProxy>().
    AddBehavior(new ProfileBehavior())) 

Proxy auto generating

To simplify and automate the process of generating proxy classes I created the simple tool which discovers the assemblies defined in configuration using Mono.Cecil*. It is the part of the PhoneCore Framework source codes and NuGet package. The SecureBox and PhoneCore Framework solutions show how to use the tool.

Result

Here are the results of interception of method invocation in SecureBox project:

334685/HealthView.png

How to use: SecureBox project

SecureBox is a Windows Phone 7.5 application which allows to store your sensitive information such as account credentials, phone numbers, passwords and prevents the access to it . Currently the development hasn't done yet, but its code can help to understand the major features of PhoneCore Framework. More details you can find in my previous article: A framework for building of WP7 application [3].

Note: it describes the earlier version of Framework and some details are outdated. See release history and documentation on phonecore.codeplex.com [4]

Lets see briefly how to use it.

Set up PhoneCore Framework

The simplest way to add PhoneCore Framework code is NuGet package:

334685/NuGet.png

The next step is configuring of the framework services. The current implementation requires you to create configuration files. In case of SecureBox:

  • application.config - root configuration file which references all other configs
  • fwk.system.config - framework initialization configuration. It defines the usage of built-in subsystems of frameworks such as DI container,tracing. You may replace them using your custom implementation and settings.
  • fwk.bootstrap.config - defines the list of default services which are initialized by bootstrapping engine at startup
  • securebox.data.config - stores the settings specific for the data layer of application

Note: do not forget to set BuildAction property as Content for your config files.

Next, add the following static resource to App.xaml:

XML
<Application.Resources>
       <core:ApplicationBootstrapper x:Key="Bootstrapper" d:IsDataSource="False"  />
       <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
 </Application.Resources>

As you see, I'm using MVVMLight as MVVM framework and its ViewModelLocator is already defined here. I changed standard ViewModelLocator logic:

C#
 public class ViewModelLocator
{
    /// <summary>
    /// Initializes a new instance of the ViewModelLocator class.
    /// </summary>
    public ViewModelLocator()
    {
    }
    
    private IPageMapping _pageMapping;
    protected IPageMapping PageMapping
    {
        get
        {
            if(_pageMapping == null)
            {
                var bootstrapper = Application.Current.Resources["Bootstrapper"] as ApplicationBootstrapper;
                IContainer container = bootstrapper.GetContainer();
                _pageMapping = container.Resolve<IPageMapping>();
            }

            return _pageMapping;
        }
    }

    #region ViewModel properties
    /// <summary>
    /// Gets the Startup property which defines the main viewmodel.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
        "CA1822:MarkMembersAsStatic",
        Justification = "This non-static member is needed for data binding purposes.")]
    public IViewModel Settings
    {
        get
        {
            return PageMapping.GetViewModel("Settings");
        }
    }
...
} 

Each property of this class returns the appropriate view model which is automatically registered if it has been defined in configuration (pageMapping node):

XML
 <pages>
    <page name="Settings">
      <uri type="relative" address="/ViewPage/SettingsViewPage.xaml" />
      <viewModel type="SecureBox.UI.ViewModel.SettingsViewPageModel, SecureBox.UI" />
    </page>
</pages> 

Next important thing is creating of custom bootstrapper plugins which will initialize and register additional logic at startup. SecureBox includes:

  • Init - reads and sets user settings up
  • DataContext - initializes and registers data context service
  • PassGen - initializes and registers password generation service

After this, you are able to use interception and other features of Framework in your project.

Summary

This article aims to show how to bring the AOP practices to the WP7 platform. The provided approach is based on custom PhoneCore Framework project which has a short history and it isn't production-ready quality yet. It is developed at my spare time in order to improve my skills in software engineering discipline.

Links

Useful books

History

2012/02/23 - Initial state of the article 

 Update 

 * Also take a look at Roslyn project. It seems like a good thing for building AOP tools 

License

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


Written By
Software Developer (Senior) Nokia
Germany Germany
Interested in design/development of framework functionality using the best patterns and practices.

Comments and Discussions

 
QuestionExcelent article Pin
hmadrigal1-Aug-12 7:10
professionalhmadrigal1-Aug-12 7:10 
AnswerRe: Excelent article Pin
Ilya Builuk1-Aug-12 8:33
Ilya Builuk1-Aug-12 8:33 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey3-Apr-12 23:58
professionalManoj Kumar Choubey3-Apr-12 23:58 
QuestionI really liked your explanation. Pin
Paulo Zemek23-Feb-12 5:15
mvaPaulo Zemek23-Feb-12 5:15 
QuestionA minor mistake... Pin
Paulo Zemek23-Feb-12 4:38
mvaPaulo Zemek23-Feb-12 4:38 
AnswerRe: A minor mistake... Pin
Ilya Builuk23-Feb-12 5:11
Ilya Builuk23-Feb-12 5:11 

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.