7

我正在使用 asp.net mvc 技术设计中型网站。所有业务逻辑都组织成 IService(如 IDomainService、IUserService、IAuthService、ITrainingService)。所有服务都使用 IRepositories。我正在使用 Ninject 1.5 将服务与控制器连接起来,它似乎工作得很好。

到目前为止,我不知道如何处理一个主题。一些服务创建上下文(每个请求) - 例如 IDomainService 创建 IUserService 所需的 DomainContext(每个请求)。ITrainingService 仅在 TrainingController 中使用,它只能由授权用户访问,并且 ITrainingService 需要 UserContext(也根据请求)来了解谁在接受培训。

这是我第一个使用 IoC 容器的项目。是否有任何设计模式或代码模式如何解决它?我想我可以使用 ActionFilters 填充上下文对象,但是如何管理它们的生命周期以及将它们放置在何处以供 IServices 访问?(以优雅的方式)

4

2 回答 2

10

我在 MVC 应用程序中专门使用了 Ninject。您使用 Ninject 完成此操作的方式是配置或绑定您的依赖项。当你这样做时,你指定你希望如何管理你的对象生命周期。在网络应用程序的大多数情况下,您的对象将按照您在问题中指出的每个请求。

我在您的问题中注意到的一件事是您的DomainContext是由IDomainService对象创建的,并被其他对象使用。如果域服务对象是DomainContext的一种工厂,那么您不会有太大问题——这将成为您如何配置 Ninject 以提供具体对象和注入依赖项的练习。

以下是有关如何构建应用程序的一般指导——请记住,我对您的接口和类没有完全了解:

public class GlobalApplication : NinjectHttpApplication {
  protected override void RegisterRoutes(RouteCollection routes) {

    // Your normal route registration goes here ...

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",                                              
        "{controller}/{action}/{id}",                           
        new { controller = "Home", action = "Index", id = "" }  
    );

  }

  // This function is resposible for creating a Ninject kernel.  This is where 
  // the magic starts to happen.  
  protected override IKernel CreateKernel() {
    var modules = new IModule[] {
                                  new AutoWiringModule(),
                                  new AutoControllerModule(
                                        Assembly.GetExecutingAssembly()),
                                  new ServiceModule()
                                };

    return new StandardKernel(modules);
  }
}

请注意,让 Ninject 工作的最简单方法是从NinjectHttpApplication类派生您的应用程序类。您需要将RegisterRoutes更改为覆盖方法,并且还需要实现一个名为CreateKernel的方法。CreateKernel方法负责返回 Ninject 内核,它本身就是 IoC 容器。

CreateKernel方法中,Ninject 提供的AutoControllerModule扫描程序集以查找 MVC 控制器类并将它们注册到容器中。这意味着现在可以通过 Ninject 注入对这些控制器的依赖项,因为它已成为应用程序的控制器提供程序。ServiceModule类是您需要创建的类,以便向 Ninject 注册所有服务。我猜它看起来像这样:

internal class ServiceModule : StandardModule {
  public override void Load() {
    Bind<IDomainService>()
      .To<MyDomainService>()
      .Using<OnePerRequestBehavior>();

    Bind<DomainContext>()
      .ToMethod( ctx => ctx.Kernel.Get<IDomainService>().CurrentDomainContext )
      .Using<OnePerRequestBehavior>();

    Bind<IService>()
      .To<MyServiceType>()
      .Using<OnePerRequestBehavior>();
  }
}

Ninject 有一个非常富有表现力的流畅的配置界面。上面请注意,每个语句基本上都将具体类与其实现的接口相关联。语句中的“使用”短语向 Ninject 内核表明该对象将仅在请求的生命周期内存活。因此,例如,这意味着任何时候在同一请求期间从 Ninject 内核请求IDomainService对象时,都会返回相同的对象。

至于您的上下文对象,我正在尝试您的域服务创建这些上下文并充当某种工厂。在这方面,我通过从IDomainService中获取名为CurrentDomainContext的属性的值来绑定上面的DomainContext类实例。这就是上面的 lambda 所完成的。Ninject 中“ToMethod”绑定的好处是您可以访问 Ninject 激活上下文对象,该对象允许您使用内核解析对象。这正是我们为了获取当前域上下文所做的事情。

接下来的步骤是确保您的对象正确接受依赖项。例如,您说ITrainingService仅在TrainingController类中使用。因此,在这种情况下,我将确保TrainingController具有接受ITrainingService参数的构造函数。在该构造函数中,您可以将对ITrainingService的引用保存在成员变量中。如:

public class TrainingController : Controller {
  private readonly ITrainingService trainingService;

  public TrainingController(ITrainingService trainingService) {
    this.trainingService = trainingService;
  }

  // ... rest of controller implementation ...
}

请记住,Ninject 已经向 Ninject 内核注册了所有控制器,因此当创建此控制器并调用它的操作时,您将通过 trainingService 成员变量获得对ITrainingService的引用。

希望这可以帮助你。使用 IoC 容器有时会变得相当混乱。请注意,我强烈建议您查看Ninject 文档——这是对 Ninject 以及 DI/IoC 概念的很好的介绍。我也省略了上面显示的 AutoWiringModule 的讨论;但是,Nate Kohari(Ninject 的创建者)在他的博客上了一篇关于此功能的精彩文章。

祝你好运!

于 2009-07-23T18:27:16.970 回答
0

我不确定我是否完全理解您的问题,希望这一点建议能有所帮助。

使用 IoC 容器时,您让容器处理对象生命周期管理。我只使用 Castle Windsor 和 StructureMap 进行依赖注入,所以我不能给你一个具体的例子来说明如何用 Ninject 做到这一点。

浏览 Ninject 文档,我认为您想查看Activation Behaviors以指定对象生命周期管理。

希望这可以帮助。

于 2009-07-21T12:15:03.860 回答