Dependency Injection Naturally

by Gael Fraiteur, 30 January 2008

This article summarizes some thoughts about the potentials of PostSharp with Dependency Injection frameworks. The aim is to support some ongoing or future discussions about loose integration of PostSharp into major DI frameworks. It does not describe something that already exist "off-the-shelf" as a downloadable plug-in.

Container or Service Locator

Most dependency injection (DI) frameworks today require instances to be constructed using a "factory", "object builder" or "container".

This approach also uses a lot of System.Reflection to set up properties. And eventually "bad" reflection: the one that breaks the object encapsulation and accesses private members. You may care or not. I do.

Another major drawback of the "factory" approach is that you cannot use the standard constructors.

This may be just annoying, but can become really blocking if your constructor is called from a part of the program that you don't have control on. For instance, what is you want to inject dependencies into a WinForms user object? When the user object will be added into a form, Visual Studio will instantiate it using the default constructor.

Anyway, why does the consumer of an object need to know that a special factory method should be used? Is our abstraction so shiny?

Of course, there is a solution: inside the constructor, initialize each dependency by a call to a service locator. It is not literally an "injection" of dependencies: dependencies are created from outside.

Container or Context

How does the service locator know how to create the dependency? Where does it take the configuration from, since the object got no reference to its container? Well, actually, the object has no container! The service locator has simply context-sensitive configuration. Each thread has its own context from which the service locator know how to create the dependency. Even better than contexts, stacks of contexts allow to have nested contexts just like we have nested transactions.

An elegant solution is to distinguish between global contexts (static ones) and local contexts (thread-specific). When a dependency is resolved, the service locator always looks for a match from the top of the context stack to the bottom. The stack of local contexts is always on the top of the stack of global contexts.

The use of local contexts makes it possible to execute unit tests in multiple threads while using different mocking implementations of dependencies.

For instance, the configuration of a local context may look like this:

using ( new LocalContext("Test") )
{
    Context.CurrentContext.Bind<ICustomerProcesses>().To<CustomerProcesses>();
    Context.CurrentContext.Bind<IRentalProcesses>().To<RentalProcesses>();

    TestRental test = new TestRental();
    test.Go();
}

Et PostSharp dans tout ça?

The drawback of the Service Locator pattern is that you need to write boiler-plate code. For each field, you have to call the service locator.

This is true for those who don't know PostSharp. But friends of aspect-oriented programming surely noticed an opportunity to let the machine generate code for you!

Wouldn't it be nice to be able to declare a dependency just like this:

class CustomerProcesses : ICustomerProcesses
{
    [Inject]
    private IRentalProcesses rentalProcesses;
}

Wouldn't it be nice to have a custom attribute that adds in all constructors calls to the service locator?

Well, for those who are comfortable with PostSharp Core, it is a matter of hours to develop it. Users of PostSharp Laos will unfortunately have to wait until they get an off-the-shelf implementation. But, promised, it will come soon!

What it means for Dependency Injection frameworks?

What does it mean for DI frameworks to be compatible with this approach?

  • First, they should provide an entry point for "service location", i.e. a method like this:
    T GetDependency<T>( RuntimeFieldHandle field,
                              object declaringInstance )
  • Second, their concept of container should be compatible with the idea of context. Ideally, they should support nesting and default/implicit container for the current thread (or globally).
  • They should have a reasonable API for the aspect weaver to understand their semantics. That is, given a FieldInfo, the DI framework should be able to tell the weaver if the field is a dependency, and if yes of what kind, so that the weaver can decide (at compile-time) if a service location method should be called at runtime, and which. Or better: given a Type, return a list of fields and properties that should be initialized, methods to be called, events to be published or subscribed to...

Apart from these relatively small side requirements (only container nesting is a little extravagant, but it's a nice-to-have both for the container- and the context- based approach), the use of an aspect weaver does not interfere too much with the design of the DI framework. So it is perfectly possible to have to have a DI framework that is AOP-enabled, but still excellent without AOP.

Using AOP in the Enterprise [Adrian Colyer]

by Gael Fraiteur, 25 January 2008
In this presentation from QCon San Francisco 2007, SpringSource CTO and AspectJ project lead Adrian Colyer discusses where Aspect-Oriented Programming (AOP) should be used, practical applications of AOP in enterprise situations such as Hibernate exception translation and automatic operation retry on nonfatal exceptions, and AOP mechanisms in Spring 2.5.

Watch Using AOP in the Enterprise (62 minutes).

[from infoQ.com]

Express Call for Feedback

by Gael Fraiteur, 22 January 2008

If you already use PostSharp Laos in any project, internal or external, I would like to hear from you and there is a chance for your company to get some (limited) visibility!

I am writing an article for the AOSD 2008 conference and I would like to augment my paper with case study (in technical, not commercial wordings). The case study will then be published separately on the website and eventually mentioned at other conferences.

So, if you think you did something interesting with PostSharp, please get back to me, I really need your feedback!

Thank you,

Gael

Under the Hood of MSBuild Integration

by Gael Fraiteur, 17 January 2008
All works so magically. Once you have PostSharp installed on your computer, your build process automatically invokes PostSharp when it thinks it should. Most of the cases, it works and it's enough. But if you have a complex build process (especially one that does not use MSBuild), you could ask: how does this work? And how can I integrate PostSharp in my non-MSBuild projects?

Getting Inserted in the MSBuild Process

Most of the stuff is in the file PostSharp-1.0.targets, a file typically installed in the directory C:\Program Files\PostSharp 1.0. From the moment you install PostSharp on your system (that is, using the Microsoft Installer package), this file is included in any project.

There is absolutely no hack in that; it uses extension points of MSBuild especially designed for this purpose (although badly documented). At the very beginning of the file C:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets, you will see the following code:

<PropertyGroup>
<CustomBeforeMicrosoftCommonTargets Condition="'$(CustomBeforeMicrosoftCommonTargets)'==''">
   $(MSBuildExtensionsPath)\v2.0\Custom.Before.Microsoft.Common.targets
</CustomBeforeMicrosoftCommonTargets>
<CustomAfterMicrosoftCommonTargets Condition="'$(CustomAfterMicrosoftCommonTargets)'==''">
  $(MSBuildExtensionsPath)\v2.0\Custom.After.Microsoft.Common.targets
</CustomAfterMicrosoftCommonTargets>
<ReportingServicesTargets Condition="'$(ReportingServicesTargets)'==''">
  $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v8.0\ReportingServices\Microsoft.ReportingServices.targets
</ReportingServicesTargets>
</PropertyGroup>
<Import Project="$(CustomBeforeMicrosoftCommonTargets)" 
        Condition="Exists('$(CustomBeforeMicrosoftCommonTargets)')"/>
 

This means that the build process loads the file c:\Program Files\MSBuild\v2.0\Custom.After.Microsoft.Common.targets, if present. He some program wants to be plugged into the standard MSBuild process, it just have to modify this file. And that's exactly what does PostSharp!

Our installer just edits this file and imports our PostSharp-1.0.targets, which gives us the opportunity to insert PostSharp tasks in the build process. How? Here also, using quite standard extension points.

Have a look, in the file Microsoft.Common.targets, at the target Compile. All its dependencies are contained in the property CompileDependsOn. And we want to modify the compilation process, we modify this property from our PostSharp-1.0.targets:

<PropertyGroup>
 <CompileDependsOn>
  $(CompileDependsOn);
  PostSharpInspectConstants;
  PostSharpInspectReferences;
  PostSharp
 </CompileDependsOn>
   </PropertyGroup>

As you can see, we have inserted three targets after other compilation targets: PostSharpInspectConstants, PostSharpInspectReferences, and PostSharp.

Invoking PostSharp Only Relevantly

We could invoke PostSharp in every single project you build on your machine, but it would not last a long time before you uninstall it, isn't it? So we have to decide, in some way, whether PostSharp should be invoked. This is the role of the targets PostSharpInspectConstants and PostSharpInspectReferences. Let's begin with the last one, which is also the most important.

The objective of the PostSharpInspectReferences target is to enable PostSharp whenever the current project is has direct or indirect references to the assembly PostSharp.Public.dll. The heuristic works in most cases: if your assembly references PostSharp.Public.dll, it's probably because it uses an aspect, so it requires to be post-processed. But it does not work always: if you develop a PostSharp plug-in or aspect library, you will surely reference PostSharp.Public.dll, but your plug-in won't need to be transformed. True, but if you develop a plug-in or an aspect library for PostSharp, you are more probable to have read the documentation, and you know how to disable automatic enabling of PostSharp based on references.

And this is one of the role of the target PostSharpInspectConstants: detecting the presence of the constant SkipPostSharp in your project. So if you want to skip PostSharp in your project, or only in a particular build configuration, simply define the SkipPostSharp constant (aka compilation symbol), and you are done!

PostSharp Has Projects, Too

Like MSBuild, PostSharp has its own notion of project: *.psproj files. And like MSBuild, PostSharp projects are based on tasks and plug-ins: a PostSharp project describes all steps that need to be performed to properly analyze and transform assemblies. You cannot just "execute PostSharp against an assembly", you always need to provide it a project.

Hopefully, PostSharp comes with a default project: Default.psproj. The default project automatically detects what is has to do based on custom attributes present in the assembly. That's why the default project is so flexible that most users don't need to be aware of this concept.

But this is an advanced post, so let's go on.

What's important here is not how automatic detection of tasks works (it is documented), but to point out that the default project must take a lot of properties from the environment, in this case from MSBuild. Here are they:

Property Meaning
Output (required) Full path of the output assembly.
ReferenceDirectory Directory by reference to which relative paths in the psproj will be resolved. We pass the root directory of the project (i.e. the directory containing your csproj file).
Configuration Debug or Release, typically. It is not used by Default.psproj.
Platform Any, typically. It is not used by Default.psproj.
SearchPath Comma-separated list of directories that have to be added to the search path while looking for plug-ins or referenced assemblies. We pass the output directory (i.e. bin\debug) as well as all reference paths defined in user-level project properties.
IntermediateDirectory Tells PostSharp where to put its own intermediate stuff. We pass typically obj\debug\PostSharp.
CleanIntermediate Whether PostSharp should clean its intermediate files after execution. We pass false.
MSBuildFullProjectPath Full path of the MSBuild project file (the csproj or vbproj). This is to solve relative paths that users could pass in custom attributes.
SignAssembly Determines whether the assembly should be signed. We take this value from MSBuild.
PrivateKeyLocation Location of the private key. We take this value from MSBuild.

All this stuff is of course passed automatically to PostSharp, so most of the time you don't have to worry about this. However, if you need to invoke PostSharp manually, this will surely interest you.

Invoking PostSharp Manually

If you are not happy with the default integration of PostSharp in the build process, there are many ways to do it differently.

If your project is built using MSBuild, you have the following options:

  • Do not install PostSharp globally, but insert it manually in each project. You will have to import PostSharp-1.0.targets in all C# or VB (or other) project requiring PostSharp. Yup, there is currently no binary package other than the installer performing global registration. But you can do this package yourself by zipping C:\Program Files\PostSharp 1.0 -- simply!.
  • Do not use our MSBuild targets, but use our MSBuild task.
  • Use the command-line utility (see below).

If you don't use MSBuild you can use the command line utility. And there is already a NAnt task in the 1.1 branch!

PostSharp from the Command Line

PostSharp comes with a command line utility. Its syntax is very simple, because nearly everything is passed as named properties:

Usage: postsharp [<options>] <project> <input> [<options>]

Options:
/P:name=value   Sets a property.
/Attach         Gives the opportunity to attach a debugger to the process.

So here is how a call to the command line may look like:

PostSharp.exe 
 Default.psproj 
 .\obj\Debug\PostSharp.Samples.Binding.exe
 /P:Output=.\obj\Debug\PostSharp\PostSharp.Samples.Binding.exe
 /P:ReferenceDirectory=P:\open\branches\1.0\Samples\PostSharp.Samples.Binding
 /P:SearchPath=bin\Debug /P:IntermediateDirectory=obj\Debug\PostSharp
 /P:CleanIntermediate=False
 /P:MSBuildProjectFullPath=P:\\Samples\PostSharp.Samples.Binding\PostSharp.Samples.Binding.csproj

Summary

I've explained how PostSharp is invoked from MSBuild and/or Visual Studio, and described the properties that are passed from MSBuild to PostSharp. Finally, I've shown how to invoke PostSharp on your own, eventually from the command line.

I hope this has been usefull to some of you and...

Happy Aspecting!

-Gael

PostSharp & Code Analysis

by Gael Fraiteur, 16 January 2008

An interesting post of David DeWinter showing how difficult it is to use FxCop/VSTS Code Analysis when PostSharp is enabled. Indeed, PostSharp does not respect all FxCop rules, and does not emit rule-disabling custom attributes.

David shows how to run FxCop before PostSharp, which involves some serious changes in MSBuild files.

Seems like a worry for the next version of PostSharp...