У меня есть тестовый рабочий код на С# + WCF+ ninject (wcf тут участвует только потому, что планируемый проект на wcf).
Вкратце: у меня есть сущность SimpleController в БД, я хочу по параметру SimpleController, получать нужную реализацию интерфейса IControllerProccesor.
Вот как это сейчас реализовано:
//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; }
}
Главный вопрос является ли строчки ниже анти паттерном и как это все дело отрефакторить в стиле DI? Или это не является анти паттерном в моем случае? Ведь ControllerProccesor объявляется в composition root и kernel используется только в нем, т.е kernel по сути же не выходит за composition root.
var param = new Ninject.Parameters.PropertyValue("Ctr", Ctr);
return kernel.Get<IControllerProccessor>(Ctr.Vendor, param).sendMsg(msg);
Второй вопрос: может это вообщем можно как то отрефакторить лучше? например так чтобы не писать в каждом методе строчки выше. Тут можно конечно создавать на каждый ctr new ControllerProccesor(ctr, kernel) - но тогда контейнер выходит за composition root - хотя так может и удобнее будет, но не зря же пишут что это анти паттерн, значит в будущем это может вылезти.
В будущем все биндинги будут в рантайме, а реализации IControllerProccesor в подключаемых в рантайме .dll (кроме ControllerProccesor - который по сути является как wrapper для всех остальных процессоров) - т.е в итоге получу систему плагинов.