Ninject相当于Autofac IIndex

时间:2017-09-12 11:49:54

标签: c# dependency-injection ninject autofac

Ninject是否等同于Autofac IIndex。换句话说,我需要获得所有具有注册名称的注册项目的集合。 在Autofac中你会这样做 编辑:做了一些改变,使事情变得更加清晰。

  static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Add>().Keyed<ICommand>("add");
            builder.RegisterType<Add>().Keyed<ICommand>("sub");

            builder.RegisterType<Calculator>().As<Calculator>();
            var container = builder.Build();
            var calc = container.Resolve<Calculator>();
            calc.Run();
        }

        public class Calculator 
        {

            IIndex<string, ICommand> commands;

            public Calculator(IIndex<string, ICommand> commands)
            {
                this.commands = commands;
            }

            public void Run() 
            {

                var result = true;
                do
                {
                  var cmd = Console.ReadLine().Split(' ');
                   ICommand command;
                   if (commands.TryGetValue(cmd[0], out command))
                   {
                       result = command.Do(cmd.Skip(1).ToArray());
                   }
                   else
                   {
                       Console.WriteLine("Command not found");
                   }


                } while (!result);
            }
        }

        public interface ICommand
        {
            bool Do(string[] data);
        }

        public class Add : ICommand
        {
            public bool Do(string[] data)
            {
                Console.WriteLine(double.Parse(data[0]) + double.Parse(data[1]));

                return false;
            }
        }

        public class Substract : ICommand
        {
            public bool Do(string[] data)
            {
                Console.WriteLine(double.Parse(data[0]) - double.Parse(data[1]));

                return false;
            }
        }

但我无法在Ninject中找到如何做到这一点。

2 个答案:

答案 0 :(得分:2)

与提供的链接一样,您打算按Key查找值。

您要搜索的内容称为named binding。绑定由名称标识。这里的问题是,你需要使用ninject的常见注入策略。

Bind<IDeviceState>().To<OnlineState>().Named("Online");
Bind<IDeviceState>().To<OfflineState>().Named("Offline");

public Modem([Named("Online")] IDeviceState state){
    _state = state;
}

通过这种方式,您可以选择IDeviceState注入NamedAttribute的实现,该实现在注入参数之前添加。但这对你来说并不完全有效,因为你想要动态创建IDeviceState。这就是你需要使用Ninject.Extensions.Factory的原因。

这样您就可以注入IDeviceStateFactory

public interface IDeviceStateFactory
{
    IDeviceState GetOnline();

    IDeviceState GetOffline();
}

那些因素是一种非常具体的语法,其中Get - 方法将解析命名绑定,如上所述。因此GetOnline()将匹配Bind<IDeviceState>().To<OnlineState>().Named("Online")绑定并返回OnlineState的实例。

告诉IoCContainer IDeviceStateFactory是一家工厂。然后,Ninject的代理系统将自动提供该接口的实现。

Bind<IDeviceStateFactory>().ToFactory();

所以最后这就是你如何使用这种技术实现你的班级Modem

public class Modem : IHardwareDevice
{
    IDeviceStateFactory _stateFactory;
    IDeviceState _currentState;

    public Modem(IDeviceStateFactory stateFactory)
    {
        _stateFactory = stateFactory;
        SwitchOn();
    }

    void SwitchOn()
    {
        _currentState = _stateFactory.GetOnline();
    }
}

使用真实密钥

但是,如果您真的想像以前一样使用DeviceState - 枚举,则可以使用here所述的方法。

绑定看起来非常像这样:

const string EnumKey = "EnumKey";

Bind<IDeviceState>().To<OnlineState>()
        .WithMetadata(EnumKey, DeviceState.Online);

使用此方法解析使用DeviceState.Online密钥

IResolutionRoot.Get<IDeviceState>(x => x.Get<DeviceState>(EnumKey) == DeviceState.Online);

但在我看来,没有理由做这么复杂的事情。我建议你忘记Autofac的整个IIndex<TKey, TSource>机制并按照ninject方式进行。

您的编辑:保留IIndex(涉及工厂扩展的解决方案)

好吧,正如IIndex并且在Ninject中确实没有这样的东西,你可以创建自己的实现,继承自IDictionary,或者只是将它定义得很相似,只定义如下:

public interface IIndex<TKey, TValue>
{
    bool TryGetValue(TKey key, out TValue value);

    TValue this[TKey key] { get; set; }
}

上面的工厂获得ICommandFactory

public interface ICommandFactory
{
    ICommand GetAdd();
    ICommand GetSubtract();
}

然后您只需要使用IIndex创建ICommandFactory的实现来解析实际的命令实例。

public class CommandIndex : IIndex<string, ICommand>
{
    private readonly ICommandFactory _factory;

    public CommandIndex(ICommandFactory factory)
    {
        _factory = factory;
    }

    public bool TryGetValue(string key, out ICommand value)
    {
        switch (key)
        {
            case "Add":
                value = _factory.GetAdd();
                break;
            case "Subtract":
                value = _factory.GetSubtract();
                break;
            default:
                value = null;
                break;
        }
        return value != null;
    }

    public ICommand this[string key]
    {
        get
        {
            ICommand value;
            TryGetValue(key, out value);
            return value;
        }
        set { throw new NotSupportedException();}
    }
}

但我真的需要问你:这件事是否必要?男人只是使用工厂。它确实变得不那么复杂了。

保留没有工厂扩展的IIndex的另一种尝试

另一种尝试可编程较少的尝试是直接使用IKernel,这可能是一种稍微不好的做法。但是,您可以解析这样的命名绑定

public class NamedBindingIndex<TValue> : IIndex<string, TValue>
{
    private readonly IKernel _kernel;

    public NamedBindingIndex(IKernel kernel)
    {
        _kernel = kernel;
    }

    public bool TryGetValue(string key, out TValue value)
    {
        value = _kernel.Get<TValue>(key); // eventually catch an Exception here
        return value != null;
    }


    public TValue this[string key]
    {
        get
        {
            TValue value;
            TryGetValue(key, out value);
            return value;
        }
        set { throw new NotSupportedException(); }
    }
}

只需像这样绑定它

Bind<IIndex<string, ICommand>>().To<NamedBindingIndex<ICommand>>();

然后你的解决方案应该同样工作。另一个好处是,这最后一次尝试不需要Ninject.Extensions.Factory。

答案 1 :(得分:0)

对于有类似问题的其他人,我仍然无法找到类似于IIndex的任何内容,而且这种情况尽可能接近。

public static class NinjectExtender
    {
        public const string Index = "INDEX";

        public static IBindingWithOrOnSyntax<TImplementation> Keyed<T, TImplementation, TKey>(this IKernel binding, TKey key) where TImplementation : T
            {
                if (!binding.CanResolve<IIndex<TKey, T>>()) 
                {
                    binding.Bind<IIndex<TKey, T>>().ToMethod(context => new Index<TKey,T>(context));
                }
                return binding.Bind<T>().To<TImplementation>().WithMetadata(Index, key);
            }

    }

    public class Index<T, M> : IIndex<T, M>
    {
        private readonly IContext context;

        public Index(IContext context)
        {
            this.context = context;
        }

        public bool TryGetValue(T key, out M value)
        {
            try
            {
                value = this[key];
                return true;
            }
            catch (KeyNotFoundException)
            {
                value = default(M);
                return false;
            }
        }

        public M this[T key]
        {
            get
            {
                try
                {
                    var service = context.Kernel.Get<M>(binding => binding.Get<T>(NinjectExtender.Index).Equals(key));

                    if (!service.Equals(default(M)))
                    {
                        return service;
                    }
                    else
                    {
                        throw new KeyNotFoundException();
                    }
                }
                catch (Exception)
                {
                    throw new KeyNotFoundException();
                }
            }
        }
    }

我有点不确定上下文的处理方式是否与Autofac相同,但我现在没有问题。

kernel.Keyed<ICommand, Add, string>("add");