Entries
RSS 2.0

Comments
RSS 2.0

Ninject Release Candidate 1

I’ve been busy the past few evenings adding some features to Ninject to move it towards the impending 1.0 release. I’ve written about a few of them before, but here’s a quick breakdown of the changes.

Transient Parameters

Previously mentioned here and here, transient parameters provide a means to manipulate the activation process for active requests to the kernel’s Get() method. Since I previously wrote about them, I’ve rewritten the internals to generalize them. Now, you create custom parameters using the IParameter interface, and extensions or your bindings can read them and alter the activation process.

Aspect-Oriented Programming (Interceptors)

I briefly talked about this before also. Since dependency injection frameworks like Ninject already control object instantiation, it’s natural to add the ability to intercept method calls on the generated instances. I’d flirted with adding support in the past, but I was wary of spending the effort to write a large proxy-generation system. I was also concerned about bloating the core library, since not everyone will use interception.

Finally, I settled on "outsourcing" the hard part — proxy generation — to already-existing libraries like LinFu.DynamicProxy and Castle DynamicProxy2. The actual interception system is baked into Ninject.Core, but in order to actually use it you have to use one of these libraries and the corresponding integration extension. (You can also create your own integration, of course!)

There are two ways to define interceptors in Ninject: static and dynamic. Static interceptors are related to a single method, and are declared via attributes. The main attribute is InterceptAttribute, which can be used directly or extended to clean up the code. You can tag either a single method with an interception attribute, in which case only that method will be intercepted — or you can put the tag on the class itself, which will cause all methods on the type to be intercepted.

With Ninject’s history of fancy fluent interfaces, though, that’s way too boring. Naturally, I had to up the ante a bit, with what I call dynamic interceptors. These are defined kind of like bindings, in a module’s Load() method. If you’ve used the conditional binding system, you’re familiar with fluent interface rooted in the When class, that can be used to create binding conditions. Well, my super-secret plan has now been un-veiled; the same fluent interface can also be used to define dynamic interceptors.

There’s now a new method, like Bind(), available in modules, called Intercept(). This means that if you want to define dynamic interceptors, you can do things like this:


Intercept<cacheInterceptor>(When.Request.Method.ReturnType.EqualTo(typeof(int)));
Intercept<countInterceptor>(When.Request.Method.Name.StartsWith("F"));

As you might guess, the first registration will cause all method calls to any objects activated via the kernel to be intercepted by a CacheInterceptor. The second registration causes all methods starting with the letter "F" to be passed through a CountInterceptor. Naturally, these are contrived examples, but you can see the power available to you.

One more cool feature ties together the two fluent interfaces in a very interesting (at least to me!) way. If you do:


When.Request.Context

You can actually write conditions against the activation context (IContext) that the object that is being intercepted was activated in! (Whew. That’s a mouthful.) This means you can make interception decisions based on a type’s activation plan, or even transient parameters passed to the Get() method.

NOTE! This also means there’s a breaking change in the conditional binding system. Anywhere you were using When, you will now have to replace with When.Context. Sorry, I’ve been trying very hard to avoid breaking changes, but this one was necessary.

The new AOP support in Ninject deserves its own blog post (or series of blog posts). It’s one of my favorite new features, and I’ll continue to write about it as time goes on.

New Tracking Component and Scopes

I’ve refactored instance tracking, used for deterministic disposal, out of the kernel itself and into a new ITracker kernel component. I’ve also introduced a new scope system, which you can use for more fine-grained control over deterministic disposal:


IKernel kernel = ...
using (kernel.BeginScope())
{
  IService service = kernel.Get<iservice>();
}

The BeginScope() method returns an IScope object, which is disposable. As long as a scope is active, any instances that are created are registered in it. When the IScope is disposed, all instances that were created therein are passed to the kernel’s Release() method.

Extension Model

One of the driving forces of Ninject has been to keep the core library as slim as possible. This has resulted in a lot of additional assemblies, and until this point there hasn’t been a natural way to load them into the kernel. The new extension model uses the concept of Ninject modules as a plug-in architecture. For example, the pub/sub messaging system, which used to live in Ninject.Messaging, is now in Ninject.Extensions.MessageBroker. To use it, all you have to do is add the MessageBrokerModule to your kernel:


IKernel kernel = new StandardKernel(new MessageBrokerModule(), ...);

As more extensions are created, they will typically exposure an IModule that can be loaded into a kernel to extend it with the functionality provided by the extension.

Cleanup and Minor Enhancements

I’m starting to revamp parts of the code to take advantage of the syntax improvements available in C# 3.0 — particularly lambda methods rather than anonymous delegates. I’ve also spent some time moving some things around in the solution to get it more organized, including tinkering with namespaces to limit the types that are exposed to code that consumes Ninject and its extensions. I’ve also provided a strong-name key and signed all of the assemblies to allow them to be used in scenarios that require it.

I’m sure there’s a bunch of other things that I’ve added, but those are the ones that I can remember at the moment. If you’d like to tinker with Ninject, there’s a new build available on the project website, or you can check out the latest version from the trunk and build it yourself. I’d like to hear what everyone has to say about the new features. I’m targeting June for a final 1.0 release, and I’d like to know what you think is missing before we can call it the big one-oh.

New Look

I’m trying out a new look on the site. Unfortunately, I had to ditch Disqus, so a few comments were lost. I was sort of disappointed it doesn’t show trackbacks/pingbacks anyway. I’m going to be tweaking things in the next few days, so let me know if you notice anything wonky. :)

Ninject and AOP

I put in some time last night and this morning to add the beginnings of interception/aspect-oriented programming support to Ninject. The development is being done in a feature branch off the main trunk, and is still pretty unstable, but feel free to check it out and let me know what you think.

As you might expect, Ninject’s interception support is attribute-driven, like most of the rest of the system. The first time a type is activated, both the class definition itself and all of its public methods are examined for any attributes that inherit from InterceptAttribute. These become part of the type’s activation plan in the same way as injection directives. If you decorate a single method with an interception attribute, only that method will be intercepted (obviously). However, if you put the attribute on the class, all of its public methods will be intercepted. There’s also a DoNotInterceptAttribute that can be added to method definitions to indicate that interceptions should be skipped for them.

More coming once I get the code a little more stable, but feel free to tinker in the meantime. By the way, if you’re interested in following the development of Ninject, or getting involved in the project, join one of the Google groups:

General discussion: http://groups.google.com/group/ninject
Development and commits: http://groups.google.com/group/ninject-dev

I’m always interested in hearing feedback and new ideas. Patches are also encouraged. :D

Context Variables in Ninject

Michael Hart asked a very good question on the Ninject user group. Specifically, he was looking for a simple way to integrate Ninject into the MS MVC IControllerFactory, to use the kernel to activate controllers when they were requested. He was doing it by creating a custom context. While this is a very creative solution, it’s really just a workaround, and gets messy quickly.

Michael’s question made me realize that Ninject’s contextual binding system is focused primarily on information that is known at compile-time, and doesn’t provide much support for information not known until run-time. To fix that problem, I’ve enhanced the transient parameters feature that I announced yesterday to include context variables. Simply put, context variables are just values that you define during your call to the kernel’s Get() method. You can then declare conditional bindings that will examine these variables and alter the activation strategy accordingly.

For example, here’s how to use context variables to activate controllers for MS MVC. The magic happens in the CreateController method of the IControllerFactory:


public IController CreateController(RequestContext context, string controllerName);

The controller name maps one-to-one with a controller type, which should be activated by the Ninject kernel. In order to let the kernel know which controller to activate, we’ll pass the controller name in as a context variable:


public IController CreateController(RequestContext context, string controllerName) {
  return Kernel.Get<icontroller>(
    With.Parameters
      .ContextVariable(“controllerName”, controllerName)
 );
}

Now the controller name is available in our activation context. Now we can declare bindings in a module that select the correct controller:


public class ControllerModule : StandardModule {
  public override Load() {
    Bind<icontroller>().To<homeController>().Only(
      When.ContextVariable(“controllerName”).EqualTo(“home”)
    );
    Bind<icontroller>().To<userController>().Only(
      When.ContextVariable(“controllerName”).EqualTo(“user”)
    );
  }
}

Now, life is good, but we can do better. Having to declare a binding for each controller is a little cumbersome. Here’s a quick-and-dirty way to automatically register all controllers in certain assemblies. It’s based off the DefaultControllerFactory in MonoRail:


public class ControllerModule : StandardModule
{
  private IEnumerable<assembly> _assemblies;

  public ControllerModule(params Assembly[] assemblies)
  {
    _assemblies = assemblies;
  }

  public override void Load()
  {
    foreach (Assembly assembly in _assemblies)
    {
      IDictionary<string, Type> controllers = FindControllers(assembly);

      foreach (KeyValuePair<string, Type> entry in controllers)
      {
        string name = entry.Key;
        Type type = entry.Value;

        Bind(typeof(IController)).To(type).Only(
          When.ContextVariable(“controllerName”).EqualTo(name)
        );
      }
    }
  }

  private IDictionary<string, Type> FindControllers(Assembly assembly)
  {
    Dictionary<string, Type> controllers = new Dictionary<string, Type>();

    foreach (Type type in assembly.GetExportedTypes())
    {
      if (!type.IsPublic || type.IsAbstract || type.IsInterface || type.IsValueType)
        continue;

      if (typeof(IController).IsAssignableFrom(type))
        controllers.Add(GetNameForController(type), type);
    }

    return controllers;
  }

  private string GetNameForController(Type type)
  {
    if (type.Name.EndsWith(“Controller”))
      return type.Substring(0, name.IndexOf(“Controller”));
    else
      return type.Name;
  }
}

To use this, just load the assemblies when you create the module, and pass them in to the constructor:


Assembly assembly = Assembly.Load(“MySite.Controllers”);
IKernel kernel = new StandardKernel(
  new ControllerModule(assembly)
);

Disclaimer! I scribbled this code in Notepad, so it may or may not be exactly correct. It may also melt your monitor, thereby singing your eyebrows off. Use at your own risk! :)

These enhancements have been committed to the trunk, so feel free to tinker and/or try to break them. I’m going to continue to work to improve the flexibility of Ninject’s binding logic and make run-time information more accessible to the contextual binding system. If anyone has any thoughts, I’d love to hear them!

Ninject Lives!

It’s been too quiet around here lately, so I figured I’d break the radio silence and talk about Ninject a little bit. I’ve let the project lie dormant a little too long, and a few enhancements and fixes were well past due.

The most interesting one is the addition of transient parameters. Several people have asked for the ability to pass arguments to the call to the kernel’s Get() method. I’ve always dodged the question because I felt like ordered un-typed arguments were clunky, dangerous, and difficult to implement. Being the fan I am of fluent interfaces, I came up with a solution. Bear in mind that this is a relatively advanced use for Ninject, and is only useful in edge cases.

Let’s say you have a simple DataService which provides access to a database via ADO.NET:


public class DataService {
  [Inject]
  public DataService(string connectionString) {
    …
  }
}

Up until now, you would be required to create a binding for the string type that would return the connection string. By using the new transient parameter support, you can directly specify the connection string you want to pass to the service when it is requested from the kernel:


IKernel kernel = …
DataService service = kernel.Get<dataService>(
  With.Parameters
    .ConstructorArgument(“connectionString”, “Data Source=localhost,…”)
);

When the kernel activates the DataService instance, it will pass the connection string you specify in the ConstructorArgument() call to the DataService’s constructor. This overrides any bindings declared for string, including conditional ones. The same thing is available for properties as well. Let’s say your DataService looks like this instead (using the fancy-pants automatic properties in C# 3.0):


public class DataService {
  [Inject]
  public string ConnectionString { get; set; }
}

You could inject a value of your choice like this:


IKernel kernel = …
DataService service = kernel.Get<dataService>(
  With.Parameters
    .PropertyValue(“ConnectionString”, “Data Source=localhost,…”)
);

Note that your properties and constructors still need to be selected for injection for the transient parameters to kick in. That means that you have to mark properties with [Inject] (or whatever attribute you’ve set in KernelOptions if you’ve overridden it).

There’s also some fun stuff for setting multiple arguments at once. Say you have a simple service like this:


public class MyService {
  [Inject]
  public MyService(string foo, int bar) {
    …
  }
}

You can declare multiple transient constructor arguments with the fluent interface:


IKernel kernel = …
MyService service = kernel.Get<myService>(
  With.Parameters
    .ConstructorArgument(“foo”, “hello”)
    .ConstructorArgument(“bar”, 42)
);

Or, you can use a dictionary:


IKernel kernel = …

Dictionary<string, object> args = new Dictionary<string, object>();
args.Add(“foo”, “hello”);
args.Add(“bar”, 42);

MyService service = kernel.Get<myService>(
  With.Parameters
    .ConstructorArguments(args)
);

Or, you can take advantage of C# 3.0 anonymous types, using a trick that’s becoming pretty popular these days. The property names of the anonymous type must correspond to the names of the constructor arguments. The value of each property will be read via reflection and injected into their corresponding argument.


IKernel kernel = …
MyService service = kernel.Get<myService>(
  With.Parameters
    .ConstructorArguments(new { foo = “hello”, bar = 42 })
);

This same support is available for defining multiple property values at once as well. These changes are coming to the trunk sometime tonight. I’m toying with the idea of relocating the Subversion repository to Beanstalk, but I haven’t decided for sure yet. Update: The changes have been committed to the repository, and I’m going to keep it hosted with Google Code. If you have any thoughts on the new transient parameter syntax, I’d love to hear them!