Tom and I are making a set of simultaneous posts about a recent addition to the EntLib 3.0 family of Application Blocks - the Policy Injection Application Block (PIAB). His post is an introduction and high-level description, but in this one I wanted to get a little bit behind the scenes and expose the 'why' behind its existence and design.

This post will make little sense as a first introduction, so

1. Go read Tom's post for a quick overview

2. Read this post for a behind-the-scenes look at this new block

Our work is still a Community Technology Preview (CTP) and we are looking for feedback! So please don't be shy.

What's the goal?

At patterns & practices we realize that a huge part of helping make enterprise applications easier to build is having good separation of concerns (SoC). Separation of concerns is about allowing the right people work in the right areas at the right time. This also allows different parts of your systems evolve at different paces.

In other words, a system that has bad separation of concerns makes it hard for people with same or different responsibilities to work in parallel, makes it hard to maintain a body of code without affecting other pieces of code, and forces decisions to be made at times that may not be the best for the team building the solution.

There are many ways to help separate concerns - via tools, platforms, methodologies, programming languages, etc. Our observation is that when the platform supports some level of separation of concerns, it is simpler for the tools and methodologies to follow suit.

Essentially the PIAB allows you to specify code that will run before and after members of your components in your application, as specified in a model, with no real code change requirements in your apps. It implements the common Composition Filters pattern.

The scenarios where this can help range from common ones - such as exception shielding, adding perfmon counters around method calls and other types of instrumentation, security checks - to quite interesting ones - versioning, multiple dispatch, parameter validation, interception for test stub assertions, and so on.

This sounds familiar...

People familiar with Aspect Oriented Programming (AOP), Aspect Oriented Software Development (AOSD) and Composition Filters (CF) should be familiar with this concept, and should resonate with many of the concepts in the PIAB. Over the years I have been personally involved in many discussions about SoC within Microsoft, including an AOSD summit we held last year. For many years I have been collecting a list of challenges customers solve with these approaches, and I can tell you it goes beyond 'instrumentation' which tends to be the first use people imagine.

I also hear many people express that this approach only applies to, and should maybe be constrained to, 'hard' boundaries in the application, such as service boundaries. The issue here is, these boundaries are quite subjective - they could be layers in an application that are not physically distributed, or they could defined by runtime usage -e.g. within a WorkItem or Module in CAB.

For example, WCF has Behaviors for adding functionality at service boundaries, and p&p has been providing blocks and examples on how to use these, if they fulfill your need. With the approach of the PIAB you can apply policies at boundaries that make sense in your design, or apply policies regardless of whether they fall on a conceptual 'boundary' or not.

So I want this to be an in-depth tour of the PIAB exposing some key design decisions, and what drove them.

What's the overall design?

The design of the PIAB can be described quite succinctly.

Imagine we have a target object with a member that we want to add some behaviors/policies to.

We use an interception mechanism to get in the way of calls going to that member, collect a list of policies that apply using a matching rules mechanism, run the chain of handlers specified by those policies in a chain of responsibility and at the other end dispatch the call to the target. Once the target is done - successfully or with exceptions - the stack unwinds, returning through each handler and finally back to the caller.

A policy is a set of handlers in a specific order, and a set of matching rules that specify to what targets it should apply.

Let's drill down into the specifics.

What is the interception mechanism and why?

First of all let me tell you that I am all for having better transparent interception in .NET as a platform. As the CLR and our languages continue to evolve, I expect this to become easier. However as of now the situation is pretty much a pick your poison scenario. Each option forces nontrivial tradeoffs. So here is one of our design choices:

All the infrastructure to gather and run policies is independent of the interception choice. We just provide a default. 

That is, while we chose a default, you can use other interception/'weaving' mechanisms and re-use the rest of the design. Or we might ship others after the fact, if there is demand.

We chose remoting proxies as our default, and you will see that in the CTP.

Choosing a default was a hard thing. Here are some of the alternatives we evaluated.

Remoting proxies : Using Real/TransparentProxy

  • Requires construction with a special factory
  • Requires class to derive from MBRO, or to have an explicit interface. 
  • Proxy is treated as the real type by the type system (.NET special cases type identity checks around these objects)

Context Bound Objects : Using ContextBoundObject and attributes

  • Allows interception of ‘new’
  • Not recommended for customer code - therefore p&p won't ship it as part of guidance!

Assembly rewriting : Taking the IL of your assembly and injecting more IL into your classes

  • Eliminates the need for proxy classes
  • Completely compatible with the type system and ‘new’
  • Can’t be used for external strong-named assemblies without delay-signing (pros and cons)
  • Not supportable by Microsoft Product Support (PSS) as of today--> ouch

Generating derived classes : Taking your classes and generating wrapping classes that derive from them

  • Requires construction with a special factory
  • Only works for virtual methods on non-sealed classes
  • Could result in type system issues

Generating inline interception code : Taking your classes and adding C#/VB code 'around' method bodies

  • Requires code to be written in a special way
  • Interception code is explicit, rather than “magic” (which has pros and cons)
  • Requires source code for intercepted objects

Other more obscure mechanisms we discarded : Things that work but we wouldn't like to ship and you wouldn't like to maintain

  • Using CLR profiler APIs
  • Using CLR JIT Debugger callbacks for runtime IL rewriting

Of course we aren't the first ones to go through these decisions. But being p&p we have to be careful and explicit about our choices as they are Microsoft's recommendation on how to tackle the problem for enterprise production environments, and we get a very broad usage. This doesn't mean other options are wrong or disrecommended, just that our tradeoffs may be driven by different forces.

The approach requires the use of a factory instead of just new for objects that have policy, which can be frustrating. If you are using our Software Factories or specifically Composite UI (CAB), Composite Web UI (CWAB) or a future Web Service Software Factory v3 preview, you are probably already using Object Builder under the hood for dependency injection, and we will provide an Object Builder strategy to make the addition of this PIAB functionality transparent to the rest of the app.

We will be running perf tests against our Reference Implementations and other real-world apps and getting impact numbers, to share performance data with you. Needless to say, using Hello World as a test scenario with this interception choice yields predictably terrible perf results. Then again , Hello World doesn't need separation of concerns; and the PIAB will not wrap classes if they don't have policy that should apply to them.

Again, you could take any other mechanism of your choice (e.g. assembly rewriting) and use the rest of the infrastructure as is.

Let's move on to the next important design element

How do you specify which policies apply to what targets?

Which policies apply to which target members is informed by a set of 'matching rules', which can be set up at runtime programmatically, or from XML config (with tool support), or from attributes (not in this CTP).

A matching rule is essentially a predicate that answers the question - does a policy apply to this member? A namespace matching rule allows you to say something like 'apply this policy if: the namespace of the class containing this method starts with MyCompany.MyApp.BusinessOperations'. You can add matching rules which get ANDed.

Why these matching rules and not just an xml schema that says these members of these types, or something like it?

The matching rules allow you to define what applies where in an expressive way, for simple or complex criteria - avoiding a one-size-fits-all model that is hard to learn for simplest cases a hard to scale to complex cases.

One of our challenges was getting a level of expressiveness in "what applies where" for cases that were application architecture specific - e.g. "All classes that are Services in CAB" or specific to usage patterns - e.g. "all classes that are presenters in view-presenter pairs". We collected a list of common criteria and two things became clear: One, our xml schema for matching rules was growing beyond what we wanted and two, by looking down our product roadmap, we were confident we didn't have all the types of criteria we wanted.

So, by having this matching rules design, each matching rule can have its own schema that is expressive and makes sense for the context. We are providing matching rules implementations for common criteria out-of-the-box, such as matching namespaces, base classes, method signatures etc. You can create matching rules that use your own XML schemas, custom languages (like those used for pointcuts by some AOP frameworks) and DSLs if you fancy. All you need to do is implement IMatchingRule.

We also have a Tag attribute and matching rule that allows you to define your own semantics of members. For example, "Apply Audit policy to every method marked as  'Critical'" is as simple as defining the Audit policy with a logging handler, and using the Tag("Critical") or your own 'Critical' attribute type on the appropriate methods:

[Tag("Critical")]
public void DoSomethingImportant()
{ }

I am not personally a fan of matching based on string comparisons of class and method names, but there is a matching rule for that too.

 

What are policies, handlers and what can they do?

A policy is a set of handlers in a specific order, and a set of matching rules that specify to what targets it should apply.

For example you can define an  "Exception Shielding" policy, with an Exception Handler and a Validation Handler. You can then define matching rules that say that "Exception Shielding" applies to "all methods of classes in the data layer"

One of the challenges in these Composition Filters (CF) or Chain of Responsibility (CoR) style of designs that can have multiple handlers, is how do you know which is the effective order of all the handlers around an object? E.g. obviously, returning cached responses before authorization is not the same as authorizing before checking the cache....

We looked at how customers dealt with this, and in many cases this ended up not being a real problem as they were willing to explicitly, manually, set the order of the handlers to make sense for the outcome. This means there is no automagic sequencing/sorting of handlers, and you define their sequence inside the policy definition in the config tool. Also, policies are ordered themselves, so if more than one policy applies to a target then you can predict the order of handlers.

A handler is the actual object that sits in the call chain before the method gets invoked. These are some interesting characteristics about handlers:

  • Handlers are executed as a chain of responsibility. This means that handlers will be on the stack by the time the target gets called. This also means handlers can decide if and when to call the next handler. For example, a validation handler may decide to return an exception without continuing the call chain. Handlers also can do work on the way in to the target, or on the way out, or both.
  • Handlers get access to the call member info, parameter infos, actual argument values, and out/return values. This means you can inspect, log and even change incoming/outgoing arguments as needed
  • Handlers can get access to exceptions, to log them, etc as appropriate.
  • Handlers have access to the target object, so they can check its state, or even do operations on it as needed.
  • Handlers can keep/share information in a loosely coupled way via a dictionary which is associated with the call. This is important as there is only one instance of configured handler per policy per appdomain. We set up the chain of responsibility behavior using delegates, so instead of keeping a pointer to the 'next' handler, handlers get a delegate reference to the Execute of the next handler, allowing us to reuse the handlers in more scenarios.The shared dictionary allows you to keep track of data not part of the call - e.g. the start time of the call; to compute total execution time on the way back.

As you see, we had a design principle, that

Handlers are powerful and can inspect and manipulate the call deeply. We won't try to protect handler authors from making mistakes by removing usefulness

What can I do with this out of the box?

We are planning to include some handlers to add utility out of the box: Validation, Logging, Exception Handling, Performance Counters, Authorization, and Caching.  We also plan to have some common matching rules.

Common Questions

This is all very scary. I can't know what will happen by looking at the code! YES - That is the whole point of separating the concerns!. If you want to have hints about what will happen in your code, you can use the tagging attributes to remind you that certain things will happen - but how they happen is up to policy.

How will this impact my debugging? Hopefully the existence of policies can assist your support and troubleshooting by providing information to help you isolate problems. Once you are debugging the code, as of now you will see extra goop. We haven't looked into providing VS tools to help abstract this out, but it is possible to build compelling tools that make you aware of the extra handlers without forcing you to step around foreign code.

Do I have to use factory methods to create my objects? Yes, with the interception mechanism we provide. You can avoid this requirement if you do the extra work of implementing another mechanism. If you do, please share back into the community!

Why don't you call this AOP? If you know about AOP, you can see where it is similar and where it differs from AOP solutions out there. The handlers fulfill the role of 'advice', the matching rules replace 'pointcut languages', and the 'weaving' is black-box based on interception. However the reality is most of our customers have not heard of AOP or AOSD. Please put yourself in their shoes for a second. We wanted a name that was reflective of the use and benefit the block brings, rather than using an unfamiliar term that only the exceptions understand. We also wanted to have no implicit expectations on the design that may be inherited from AO solutions. However, if you think this is a dumb move, please holler on our CodePlex community.

OK, If I use this now what is the road moving forward, how will other pieces of the .NET platform/language support this scenario? This is where I remind you that p&p is not the CLR team, and there is nothing specific announced to date targeting this scenario as part of the .NET platform. One could imagine using C# partial methods for some cases where you need to call out to PIAB handlers in-line, and maybe simplify code-generation of wrapper classes. Dynamic languages provide some runtime manipulation of members, e.g. replacing a member implementation with a wrapping member implementation, or augmenting the implementation with the code that today resides in the handlers. All these just would be enabling technologies to simplify creation of PIAB like infrastructure pieces.

I am using an AOP solution today. Do I care about this? Great! You might find in the provided handlers some useful sample code that can help you plug EntLib blocks into your own infrastructure. Keep using what you have if you are happy with it! Also, if you have built infrastructure pieces yourself for similar purposes, check our EULA and if all is well feel free to reuse pieces, or feel free to post handlers you may have built adapted for the PIAB, or other code, in Codeplex.

Wrapping up...

We hope you will find this to be a useful addition to your toolkit. Sorry for not announcing this earlier, but we did start development 3 weeks ago and we weren't sure this would make it in until this week.

Thanks a lot to Christa Schwanninger (Siemens Corporate Technology) for her feedback and refinements to this blog post.