- Introduction
- 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:
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:
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:
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:
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:
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:
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:
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):
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:
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:
namespace PhoneCore.Framework.IoC.Interception.Behaviors
{
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:
using System;
using PhoneCore.Framework.Configuration;
namespace PhoneCore.Framework.IoC.Interception.Behaviors
{
public interface IBehavior: IConfigurable
{
string Name { get; set; }
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:
<interception>
-->
<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:
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:
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:
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:
<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:
public class ViewModelLocator
{
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
[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):
<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