开发者

Runtime Binding: A simple task runner with ninject?

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 );
        }
    }
}


I like Ruben's answer, but it could be as simple as using the Type names of the tasks you want on the command line:

public static void Main( string[] args )
{
    var kernel = new StandardKernel();
    var tasks = new List<ITask>();
    foreach(var taskName in args)
    {
        tasks.Add( kernel.Get( Type.GetType( taskName ) ) );
    }

    doSomething(tasks);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜