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中找到如何做到这一点。
答案 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
并且在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();}
}
}
但我真的需要问你:这件事是否必要?男人只是使用工厂。它确实变得不那么复杂了。
另一种尝试可编程较少的尝试是直接使用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");