【发布时间】:2017-05-07 09:56:41
【问题描述】:
简而言之:我想通过实体中的道具解析接口。 我有自托管的 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 等)移动到单独的程序集中并进行运行时绑定。 (这里我要插件系统)
如果有任何改进我的代码的建议,我也将不胜感激。非常感谢。
修改实现
经过思考过程,我得出以下结论: 我删除了 ControllerProcessor 类,而是创建了 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类):
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;
}
}
【问题讨论】:
-
对容器的依赖是否是[an anti-pattern](blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern) 取决于role it plays in your application。简而言之:如果依赖容器的代码是Composition Root的一部分,就可以了;否则就是个问题。
-
@Steven Thx,我阅读了这些文章,所以我在这里。如何重构我现有的代码?
-
您的
ControllerProccessor似乎没有任何业务逻辑,因此您可以轻松地将ControllerProccessor移动到您的Composition Root 中;这将解决问题。 -
@Steven 感谢您的帮助。 program.cs 不是组合根吗?我在绑定中定义了 ControllerProccesor,而且我只在构造函数上使用它。而我在 ControllerProccessor
return kernel.Get<IControllerProccessor>(Ctr.Vendor, param).sendMsg(msg);中使用的事实不被视为服务定位器?
标签: c# wcf dependency-injection ninject service-locator