I'm writing a simple task runner application.
I have a bunch of classes that Implement an ITask interface :
public interface ITask
{
void Run();
}
I'm writing a simple console app that creates instances of ITasks and then calls Run() on each of them. The task implementations use constructor injection so I would like to use ninject.
I would like to be able to specifiy at runtime which tasks to run and therefore which implementation(s) of ITask to activate.
I was thinking that I could put the concrete types into my app.config then at run time I could get ninject to build me an ITask array from it. Failing this I could specify the tasks I want to run on the command line.
To me this sounds like a fairly straight forward case for ninject but I've been unable to find how to get ninject to bind from configuration or a string.
Can anybody point me in the right direction?
There are extensions for ninject that handle things like xml configuration.
I'd be careful about mixing up the programming bits from the config a la Spring XML config though - there's no need to move to XML config just to allow people to configure things in a .config file. I suggest reading an XML config section loader that serializes in a class that expresses that at a higher level instead.
You'd use the metadata mechanism on your Binding registrations and then indicate how to filter the set of tasks based on that.
e.g., repurposing @Ian Davis's answer (go read it and upvote it now!):
string metaDataKey = "key";
kernel.Bind<IWeapon>().To<Shuriken>().WithMetadata(metaDataKey, true);
kernel.Bind<IWeapon>().To<Sword>().WithMetadata(metaDataKey, false);
kernel.Bind<IWeapon>().To<Knife>();
bool? theOneIWant = null; // or true or false - i.e., the distillation of what your config says
Func<IMetadata> myConfigSaysIWantOneLikeThatPredicate= metadata =>
metadata.Has(metaDataKey) == theOneIWant != null
&& metadata.Get<bool>(metaDataKey) == theOneIWant.Value
var weapons = kernel.Get<IEnumerable<IWeapon>>( myConfigSaysIWantOneLikeThatPredicate );
// the above will generate a single item given the bindings above, but you get the picture - this generates an arbitrary length list
foreach(var weapon in weapons)
weapon.Fire();
If all you're looking for is to be able to name them, there's a shorthand replacement for WithMetadata
called Named()
and an overload for .Get<T>()
with a name string parameter, which would let you achieve @dave thieben's simplicity without your invocations being hardwired to Type names.
EDIT: Sample, see comments:
using Ninject;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace ninjectmess
{
public class Class1
{
Some junk classes
public interface ITask
{
}
public class Aasdsdaadsdsa : ITask
{
}
public class Bdsadsadasdsadsadsa : ITask
{
}
public class Cddsadasdsadasdas : ITask
{
}
the actual test
[Fact]
public void TestMethod()
{
var k = new StandardKernel();
k.Bind<ITask>().To<Aasdsdaadsdsa>().Named( "A" );
k.Bind<ITask>().To<Bdsadsadasdsadsadsa>().Named( "B" );
k.Bind<ITask>().To<Cddsadasdsadasdas>().Named( "C" );
var wanted = new string[] { "A", "C" };
var tasks = k
.GetAll<ITask>( metadata => wanted.Contains( metadata.Name ))
.ToList();
Assert.Equal( 2, tasks.Count );
tasks.ForEach( Console.WriteLine );
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With