组合根与服务定位器

Composition root vs service locator

我一直在阅读这两种解决依赖关系的方法,并找到了一些 ninject 实现的示例代码。

对于服务定位器,遵循类似

的内容
 public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
 {
    IKernel kernel;

    public NinjectDependencyResolver(IKernel kernel)
        : base(kernel)
    {
        this.kernel = kernel;
    }

    public IDependencyScope BeginScope()
    {
        return new NinjectDependencyScope(kernel.BeginBlock());
    }
}

public class NinjectDependencyScope : IDependencyScope
{
    IResolutionRoot resolver;

    public NinjectDependencyScope(IResolutionRoot resolver)
    {
        this.resolver = resolver;
    }

    public object GetService(Type serviceType)
    {
        if (resolver == null)
            throw new ObjectDisposedException("this", "This scope has been disposed");

        return resolver.TryGet(serviceType);
    }

    public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
    {
        if (resolver == null)
            throw new ObjectDisposedException("this", "This scope has been disposed");

        return resolver.GetAll(serviceType);
    }

    public void Dispose()
    {
        IDisposable disposable = resolver as IDisposable;
        if (disposable != null)
            disposable.Dispose();

        resolver = null;
    }
}

开箱即用class

    public static class NinjectWebCommon 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        try
        {
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

            RegisterServices(kernel);
            GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<MembersService>().To<MembersService>();
        kernel.Bind<MemberContext>().To<MemberContext>();
    }  

对于合成根,我遵循了 - https://gist.github.com/paigecook/3860942

 public class NinjectKernelActivator: IHttpControllerActivator
{
    private readonly IKernel _kernel;

    public NinjectKernelActivator(IKernel kernel)
    {
        _kernel = kernel;
    }

    public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        var controller = (IHttpController) _kernel.Get(controllerType);

        request.RegisterForDispose( new Release(()=> _kernel.Release(controller)));

        return controller;
    }
}

internal class Release : IDisposable
{
    private readonly Action _release;

    public Release(Action release)
    {
        _release = release;
    }

    public void Dispose()
    {
        _release();
    }
}

并在 NinjectWebCommon 中对 Create(..) 进行了一次更改。

                //GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
            GlobalConfiguration.Configuration.Services.Replace(
              typeof(IHttpControllerActivator),
                new NinjectCompositionRoot(kernel));

编辑

控制器和服务创建

public class MembersController : ApiController
{
    private readonly IMembersService _membersService;

    public MembersController(IMembersService membersService)
    {
        _membersService = embersService;
    }

    ...
}


public class MembersService : IMembersService
{
    private readonly MembersContext _context;

    public MembersService(MemberContext context)
    {
        _context = context;
    }

    ...
}

我是否正确地实现了组合根? 我真的不明白这两种方法有什么区别?

Composition root(这是你应该如何进行依赖注入)和服务定位器之间的区别是组合根应该在应用程序的一个地方(尽可能靠近应用程序的入口点)。这并不意味着它只会被调用一次。例如,在 MVC/WebAPI 的情况下,组合根的好地方是控制器工厂,它为应用程序接收的每个 HTTP 请求创建控制器。关键是在控制器工厂中实现的组合根应该创建处理请求所需的整个对象图(控制器及其所有依赖项),以便在此请求期间不需要从容器中单独解析其他依赖项。

另一方面,服务定位器是您在需要时从服务定位器检索依赖项的方法。服务定位器成为您应用程序中的环境上下文(通常提供静态 ServiceLocator.Get<T>() 方法)。服务定位器与 Dependency Injection 相反,因为不是注入依赖项,而是在需要时检索它们。这意味着在整个应用程序代码中调用 ServiceLocator.Get<T>() 方法,并且应用程序的所有层都依赖于服务定位器。这种方法有几个缺点,其中之一是它使代码更难进行单元测试,因为所有测试都需要与相同的全局服务定位器交互 class 以设置 class 下的虚假依赖项测试。

你的 NinjectKernelActivator 组合根的实现是正确的,假设你没有在一些 public static 属性 的其他地方公开 IKernel 以便稍后使用它来获取你的依赖项不要注射。