在下一种情况下如何避免 IoC 定位器?
How I can to avoid IoC locator in next case?
简而言之:我想通过实体中的道具解析接口。
我有自己托管的 wcf 并使用 ninject 作为 DI。
例如我的工作代码:
//program.cs
...
private static StandardKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<IDbConnectionFactory>().ToMethod(c =>
new OrmLiteConnectionFactory(
conString,
SqlServerDialect.Provider))
.InSingletonScope();
kernel.Bind<IControllerProccessor>().To<ControllerProccessor>()
.WhenInjectedInto<HelloWorldService>().WithConstructorArgument(kernel);
kernel.Bind<IControllerProccessor>().To<Vendor1Proccessor>()
.Named("vendor1");
kernel.Bind<IControllerProccessor>().To<Vendor2Proccessor>()
.Named("vendor2");
return kernel;
}
...
//IControllerProccessor.cs
public interface IControllerProccessor
{
SimpleController Ctr { get; set; }
bool sendMsg(string msg);
}
//Vendor1Proccessor.cs
public class Vendor1Proccessor : IControllerProccessor
{
public SimpleController Ctr {get; set;}
public bool sendMsg(string msg)
{
//specific to vendor code, for example calls to vendor1 SDK
Console.WriteLine("Controller id: {0} vendor:{1} recivied msg: {2}",
Ctr.Id,
"Vendor1Class",
msg);
return true;
}
}
//Vendor2Proccessor.cs
public class Vendor2Proccessor : IControllerProccessor
{
public SimpleController Ctr { get; set; }
public bool sendMsg(string msg)
{
//specific to vendor code, for example calls to vendor1 SDK
Console.WriteLine("Controller id: {0} vendor:{1} recivied msg: {2}",
Ctr.Id,
"Vendor2Class",
msg);
return true;
}
}
//ControllerProccessor.cs
public class ControllerProccessor : IControllerProccessor
{
public SimpleController Ctr {get; set;}
private readonly IKernel kernel;
public ControllerProccessor(IKernel _kernel)
{
kernel = _kernel;
}
public bool sendMsg(string msg)
{
var param = new Ninject.Parameters.PropertyValue("Ctr", Ctr);
return kernel.Get<IControllerProccessor>(Ctr.Vendor, param).sendMsg(msg);
}
}
//HelloWorldService.cs
public class HelloWorldService : IHelloWorldService
{
private readonly IDbConnectionFactory dbFactory;
private readonly IControllerProccessor ctrProccessor;
public HelloWorldService(IDbConnectionFactory _dbFactory, IControllerProccessor _ctrProccesor)
{
dbFactory = _dbFactory;
ctrProccessor = _ctrProccesor;
}
public bool sendMsgToAllControllers(string msg)
{
var db = dbFactory.Open();
var controllers = db.Select<SimpleController>();
foreach(var ctr in controllers)
{
ctrProccessor.Ctr = ctr;
ctrProccessor.sendMsg(msg);
}
db.Close();
return true;
}
}
//SimpleController.cs
[DataContract]
[Alias("SimpleController")]
public class SimpleController
{
[AutoIncrement]
[DataMember]
public int? Id { get; set; }
[DataMember]
public string Vendor { get; set; }
}
当我调用 sendMsgToAllControllers("TEST_MESSAGE") 控制台输出时:
Controller id: 2 vendor:Vendor1Class recivied msg: TEST MESSAGE
Controller id: 3 vendor:Vendor2Class recivied msg: TEST MESSAGE
Controller id: 4 vendor:Vendor2Class recivied msg: TEST MESSAGE
我如何重构上述实现以使其采用 DI 风格,并且不使用 IoC 定位器反模式(或者在我的情况下这不是反模式)?
将来我将在单独的程序集中移动实现(vendor1、vendor2 等)并进行运行时绑定。 (这里我要插件系统)
我也非常感谢任何改进我的代码的建议。非常感谢。
实施的修改
经过思考,我得出以下结论:
我删除了 ControllerProccessor class 而不是我创建了 ControllerProcessorFactory:
public class ControllerProcessorFactory : IControllerProcessorFactory
{
private readonly IResolutionRoot resolutionRoot;
public ControllerProcessorFactory(IResolutionRoot _resolutionRoot)
{
resolutionRoot = _resolutionRoot;
}
public IControllerProcessor Create(SimpleController ctr)
{
IControllerProcessor processor = resolutionRoot.Get<IControllerProcessor>(ctr.Vendor);
processor.Ctr = ctr;
return processor;
}
}
在绑定中:
kernel.Bind<IControllerProcessorFactory>().To<ControllerProcessorFactory>();
kernel.Bind<IControllerProcessor>().To<Vendor1Processor>()
.Named("vendor1");
kernel.Bind<IControllerProcessor>().To<Vendor2Processor>()
.Named("vendor2");
用法(wcf class):
public class HelloWorldService : IHelloWorldService
{
private readonly IDbConnectionFactory dbFactory;
private readonly IControllerProcessorFactory ctrProcessorFactory;
public HelloWorldService(IDbConnectionFactory _dbFactory, IControllerProcessorFactory _ctrProcFactory)
{
dbFactory = _dbFactory;
ctrProcessorFactory = _ctrProcFactory;
}
public bool sendMsgToAllControllers(string msg)
{
var db = dbFactory.Open();
var controllers = db.Select<SimpleController>();
foreach(var ctr in controllers)
{
var ctrProcessor = ctrProcessorFactory.Create(ctr);
ctrProcessor.sendMsg(msg);
}
db.Close();
return true;
}
}
查看文档中的动态模块加载。
即
kernel.Load("*.dll");
但请确保在启动时执行此操作,以免在运行时使系统不堪重负。
至于你的模式,我建议在内核上使用 GetAll() 方法,因为这会给你更多的灵活性和控制
IEnumerable<IControllerProccessor> processors = kernel.GetAll<IControllerProccessor>();
foreach(var processor in processors)
processor.sendMsg(....);
kernel.Get<IControllerProccessor>(Ctr.Vendor, param)
是服务定位符,
但话又说回来,这并不总是意味着 "it" 是个问题。如果它很容易互换,那就没什么大不了的(至少有些人是这么认为的)。容易互换?创建一个特定的工厂接口,其唯一职责是 return 所有处理器。
然后实施将完全由 return kernel.Get<IControllerProccessor>(Ctr.Vendor, param);
组成。只要实现是组合根的一部分,这种对 ninject 的特定依赖就可以了。
更短的选择
现在,老实说,您的设计对我来说看起来过于复杂,但话又说回来,我不知道所有的细节。所以现在我只使用 name 参数,但你可以随意添加参数(几乎),它仍然有效:
只需注入一个 Func<string, IControllerProcessor>
而不是内核:
public ControllerProccessor(
Func<string,IControllerProcessor>> controllerProcessorFactory)
然后您可以按如下方式指定绑定:
private static IControllerProcessor CreateSpecificControllerProcessor(
IResolutionRoot resolutionRoot, string vendorName)
{
return resolutionRoot.Get<IControllerProcessor>(vendorName);
}
Bind<Func<IControllerProcessor>()
.ToConstant(vendorName => CreateSpecficiControllerProcessor(this.Kernel, vendorName));
不指定 Func
的绑定,可能 可以使用 Ninject.Extensions.Factory。但是请注意,当您使用此扩展时,将无法再手动 Bind
任何 Func
(绑定将被扩展生成机制覆盖)。
简而言之:我想通过实体中的道具解析接口。 我有自己托管的 wcf 并使用 ninject 作为 DI。 例如我的工作代码:
//program.cs
...
private static StandardKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<IDbConnectionFactory>().ToMethod(c =>
new OrmLiteConnectionFactory(
conString,
SqlServerDialect.Provider))
.InSingletonScope();
kernel.Bind<IControllerProccessor>().To<ControllerProccessor>()
.WhenInjectedInto<HelloWorldService>().WithConstructorArgument(kernel);
kernel.Bind<IControllerProccessor>().To<Vendor1Proccessor>()
.Named("vendor1");
kernel.Bind<IControllerProccessor>().To<Vendor2Proccessor>()
.Named("vendor2");
return kernel;
}
...
//IControllerProccessor.cs
public interface IControllerProccessor
{
SimpleController Ctr { get; set; }
bool sendMsg(string msg);
}
//Vendor1Proccessor.cs
public class Vendor1Proccessor : IControllerProccessor
{
public SimpleController Ctr {get; set;}
public bool sendMsg(string msg)
{
//specific to vendor code, for example calls to vendor1 SDK
Console.WriteLine("Controller id: {0} vendor:{1} recivied msg: {2}",
Ctr.Id,
"Vendor1Class",
msg);
return true;
}
}
//Vendor2Proccessor.cs
public class Vendor2Proccessor : IControllerProccessor
{
public SimpleController Ctr { get; set; }
public bool sendMsg(string msg)
{
//specific to vendor code, for example calls to vendor1 SDK
Console.WriteLine("Controller id: {0} vendor:{1} recivied msg: {2}",
Ctr.Id,
"Vendor2Class",
msg);
return true;
}
}
//ControllerProccessor.cs
public class ControllerProccessor : IControllerProccessor
{
public SimpleController Ctr {get; set;}
private readonly IKernel kernel;
public ControllerProccessor(IKernel _kernel)
{
kernel = _kernel;
}
public bool sendMsg(string msg)
{
var param = new Ninject.Parameters.PropertyValue("Ctr", Ctr);
return kernel.Get<IControllerProccessor>(Ctr.Vendor, param).sendMsg(msg);
}
}
//HelloWorldService.cs
public class HelloWorldService : IHelloWorldService
{
private readonly IDbConnectionFactory dbFactory;
private readonly IControllerProccessor ctrProccessor;
public HelloWorldService(IDbConnectionFactory _dbFactory, IControllerProccessor _ctrProccesor)
{
dbFactory = _dbFactory;
ctrProccessor = _ctrProccesor;
}
public bool sendMsgToAllControllers(string msg)
{
var db = dbFactory.Open();
var controllers = db.Select<SimpleController>();
foreach(var ctr in controllers)
{
ctrProccessor.Ctr = ctr;
ctrProccessor.sendMsg(msg);
}
db.Close();
return true;
}
}
//SimpleController.cs
[DataContract]
[Alias("SimpleController")]
public class SimpleController
{
[AutoIncrement]
[DataMember]
public int? Id { get; set; }
[DataMember]
public string Vendor { get; set; }
}
当我调用 sendMsgToAllControllers("TEST_MESSAGE") 控制台输出时:
Controller id: 2 vendor:Vendor1Class recivied msg: TEST MESSAGE
Controller id: 3 vendor:Vendor2Class recivied msg: TEST MESSAGE
Controller id: 4 vendor:Vendor2Class recivied msg: TEST MESSAGE
我如何重构上述实现以使其采用 DI 风格,并且不使用 IoC 定位器反模式(或者在我的情况下这不是反模式)?
将来我将在单独的程序集中移动实现(vendor1、vendor2 等)并进行运行时绑定。 (这里我要插件系统)
我也非常感谢任何改进我的代码的建议。非常感谢。
实施的修改
经过思考,我得出以下结论: 我删除了 ControllerProccessor class 而不是我创建了 ControllerProcessorFactory:
public class ControllerProcessorFactory : IControllerProcessorFactory
{
private readonly IResolutionRoot resolutionRoot;
public ControllerProcessorFactory(IResolutionRoot _resolutionRoot)
{
resolutionRoot = _resolutionRoot;
}
public IControllerProcessor Create(SimpleController ctr)
{
IControllerProcessor processor = resolutionRoot.Get<IControllerProcessor>(ctr.Vendor);
processor.Ctr = ctr;
return processor;
}
}
在绑定中:
kernel.Bind<IControllerProcessorFactory>().To<ControllerProcessorFactory>();
kernel.Bind<IControllerProcessor>().To<Vendor1Processor>()
.Named("vendor1");
kernel.Bind<IControllerProcessor>().To<Vendor2Processor>()
.Named("vendor2");
用法(wcf class):
public class HelloWorldService : IHelloWorldService
{
private readonly IDbConnectionFactory dbFactory;
private readonly IControllerProcessorFactory ctrProcessorFactory;
public HelloWorldService(IDbConnectionFactory _dbFactory, IControllerProcessorFactory _ctrProcFactory)
{
dbFactory = _dbFactory;
ctrProcessorFactory = _ctrProcFactory;
}
public bool sendMsgToAllControllers(string msg)
{
var db = dbFactory.Open();
var controllers = db.Select<SimpleController>();
foreach(var ctr in controllers)
{
var ctrProcessor = ctrProcessorFactory.Create(ctr);
ctrProcessor.sendMsg(msg);
}
db.Close();
return true;
}
}
查看文档中的动态模块加载。 即
kernel.Load("*.dll");
但请确保在启动时执行此操作,以免在运行时使系统不堪重负。 至于你的模式,我建议在内核上使用 GetAll() 方法,因为这会给你更多的灵活性和控制
IEnumerable<IControllerProccessor> processors = kernel.GetAll<IControllerProccessor>();
foreach(var processor in processors)
processor.sendMsg(....);
kernel.Get<IControllerProccessor>(Ctr.Vendor, param)
是服务定位符,
但话又说回来,这并不总是意味着 "it" 是个问题。如果它很容易互换,那就没什么大不了的(至少有些人是这么认为的)。容易互换?创建一个特定的工厂接口,其唯一职责是 return 所有处理器。
然后实施将完全由 return kernel.Get<IControllerProccessor>(Ctr.Vendor, param);
组成。只要实现是组合根的一部分,这种对 ninject 的特定依赖就可以了。
更短的选择
现在,老实说,您的设计对我来说看起来过于复杂,但话又说回来,我不知道所有的细节。所以现在我只使用 name 参数,但你可以随意添加参数(几乎),它仍然有效:
只需注入一个 Func<string, IControllerProcessor>
而不是内核:
public ControllerProccessor(
Func<string,IControllerProcessor>> controllerProcessorFactory)
然后您可以按如下方式指定绑定:
private static IControllerProcessor CreateSpecificControllerProcessor(
IResolutionRoot resolutionRoot, string vendorName)
{
return resolutionRoot.Get<IControllerProcessor>(vendorName);
}
Bind<Func<IControllerProcessor>()
.ToConstant(vendorName => CreateSpecficiControllerProcessor(this.Kernel, vendorName));
不指定 Func
的绑定,可能 可以使用 Ninject.Extensions.Factory。但是请注意,当您使用此扩展时,将无法再手动 Bind
任何 Func
(绑定将被扩展生成机制覆盖)。