Preface
NJection provides a way to inject .NET code at runtime using configuration files.
The library defines a set of building blocks which are equivalent to any programming language fundamental elements.
Using these building blocks in an xml fashion configuration files, one can construct any desired flow of execution.
NJection is an open source project. Documentation, Sources, binaries and samples can be found at http://www.njection.net/
Motivation
After seeing one of the early posts about C# 4.0 expression trees, I was amazed to see how easily one can craft new blocks of code without the need to use Reflection.Emit.
The first thing came to my mind was the possibility to configure these blocks of code using external sources, thus, allowing me to inject different pieces of code at runtime.
Design Considerations
The main idea was to build an object tree that reflects the configuration file.
Each node in the tree will be correlated to the element representing the expression.
The node will parse and create the values configured in the file and eventually construct an expression of a specific type. Due to the fact that .NET 4.0 supports over 100 types of expressions I needed a way to decide which expression to construct at a given time. I've adopted the idea to decide which expression to construct from the System.Linq.Expression.ExpressionVisitor and created the ExpressionBuilder class whose functionality is required to traverse the tree.
NJection provides 28 expressions to work with, including the Binary and the Unary expressions, each of which of the two gives the option the create about 20 different expressions. Two special expressions in NJection are the Lambda and the Block. Each one of them represents a block that accepts parameters exposed as expressions, and contains a sequence of expressions where variables can be defined. Defining a variable can be done using the definitions section and defining an argument for the block can be done using the arguments section. If a variable has been declared in a specific block then it is accessible for all other expressions defined within that block including nested blocks. Due to the fact that the structure of each of expression is a hierarchy of certain expressions and each expression may be composed of other expressions, when a complex expression detects that a different expression is contained within it, it calls its Resolve function. The function basically tries to detect if the expression has already been defined in the current scope up to the root of the tree which is the Lambda. If it finds the expression it fetches it, otherwise the ExpressionBuilder.Visit static method is called which resolves the type of the expression to construct, and instantiate the relevant NJection expression.
Confiuration Files
All configuration files are xml based.
This is an example of an xml configuration to invoke WriteLine of the static class Console.
The Root expression element must be of type Lambda and the typeof attribute can only be Action or Func.
Traversing Confiuration Files
In order to execute the code the configuration file should be read and parsed.
It can be done using the ExpressionBuilder's Traverse static method which returns a lambda expression that is equivalent to the root element of the configuration file.
LambdaExpression ex = ExpressionBuilder.Traverse<LambdaExpression>(@"C:\Example.config");
or
Expression<Action<string>> ex = ExpressionBuilder.Traverse<Expression<Action<string>>>(@"C:\Example.config");
Once a lambda expression is retrieved it can be compiled and run:
Action<string> action = ex.Compile();
action("Hello World!");
Or incase the traverse method retrieved a LambdaExpression object:
Delegate action = ex.Compile();
action.DynamicInvoke("Hello World!");
Documentation
Detailed documentation can be found at:
http://www.njection.net