我正在创建一个后台任务控制器,如下所示:
public class TaskController
{
private TaskBase task;
public TaskController(ITask task)
{
this.task = task;
}
public void DoSomething()
{
task.DoSomething();
}
}
ITask
界面:
interface ITask
{
void DoSomething();
}
TaskBase
abtract class:
public abtract class TaskBase : ITask
{
\\some common fields/properties/methods
public void DoSomething()
{
\\perform action here
}
}
Task
实施:
public class Task1 : TaskBase
{
public Task1(string arg, int arg1)
{
}
}
public class Task2 : TaskBase
{
public Task2(bool arg, double arg)
{
}
}
这是一个如何使用它的例子:
public void DoTask(string arg, int arg1)
{
Task1 task = new Task1(arg, arg1);
TaskController controller = new TaskController(task);
controller.DoSomething();
}
如您所见,我在这种方法中使用手动注射。现在我想转而使用像NInject这样的IoC,但在做了一些研究之后,还有两件事情让我感到烦恼。
1. How can I tell the binding which concrete task to use in particular context?
2. How to pass dynamic arguments (`arg` and `arg1` on above example) to `Bind<T>` method
注意: 如果你看到我的问题需要一个downvote,请留下一些评论,以帮助我避免将来犯错误
答案 0 :(得分:2)
您遇到的问题是由您的设计引起的。如果你改变你的设计,问题就会消失。你应该做一些事情:
DoSomething
方法,同时它们还包含需要执行的数据。如果从行为中提取数据,您将获得以下内容:
// Definition of the data of Task1
public class Task1Data
{
public string Arg;
public int Arg1;
}
// The behavior of Task1
public class Task1 : ITask<Task1Data> {
public void Handle(TTask1Data data) {
// here the behavior of this task.
}
}
此处每个任务都实现了通用ITask<TTaskData>
接口:
public interface ITask<TTaskData>
{
Handle(TTaskData data);
}
有了这个设计,我们现在可以按如下方式使用它:
private ITask<Task1Data> task1;
public Consumer(ITask<Task1Data> task1) {
this.task1 = task1;
}
public void DoTask(string arg, int arg1)
{
task1.Handle(new Task1Data { Arg = arg, Arg1 = arg1 });
}
我们按如下方式注册我们的任务:
kernel.Bind<ITask<Task1Data>>().To<Task1>();
kernel.Bind<ITask<Task2Data>>().To<Task2>();
kernel.Bind<ITask<Task3Data>>().To<Task3>();
虽然我对Ninject不是很有经验,但我确信有一种方法可以将这些注册转换为方便的单行。
这种设计有很多优点。例如,它可以更轻松地添加横切关注点。例如,您可以创建一个包含事务中每个任务的通用装饰器,如下所示:
public class TransactionTaskDecorator<T> : ITask<T> {
private readonly ITask<T> decoratee;
public TransactionTaskDecorator(ITask<T> decoratee) {
this.decoratee = decoratee;
}
public void Handle(T data) {
using (var scope = new TransactionScope()) {
this.decoratee.Handle(data);
scope.Complete();
}
}
}
这样的装饰器可以在消费者不必了解它的情况下应用,因为它只取决于ITask<T>
接口。
您还可以添加允许在后台线程中执行任务的装饰器:
public class BackgroundTaskDecorator<T> : ITask<T> {
private readonly Func<ITask<T>> decorateeFactory;
private readonly ILogger logger;
public TransactionTaskDecorator(Func<ITask<T>> decorateeFactory, ILogger logger) {
this.decorateeFactory = decorateeFactory;
this.logger = logger;
}
public void Handle(T data) {
Task.Factory.StartNew(() =>
{
try {
// We're running on a different thread, so we must create the task here.
var decoratee = this.decorateeFactory.Invoke();
decoratee.Handle(data);
} catch (Exception ex) {
this.logger.Log(ex);
}
}
}
}
您可以详细了解此设计here。
答案 1 :(得分:1)
1)使用像这样的命名属性
public TaskController([Named("MyName")] ITask task)
然后在NinjectModule
Bind<ITask>().To<Task1>().Named("MyName");
2)认为你可以使用与上面相同的方法