是否可以在应用程序启动时不使用服务定位器来实现依赖注入?

时间:2017-04-28 09:43:36

标签: c# dependency-injection inversion-of-control service-locator

我对服务定位器和依赖注入的概念非常熟悉,但是有一件事让我感到困惑,即为应用程序实现依赖注入,我们必须在开始时使用某种服务定位器。请考虑以下代码,假设我们有一些简单的DAL类:

public class UserProviderSimple : IUserProvider
{
    public void CreateUser(User user)
    {
        //some code to user here
    }
}

然后在Business Logig Layer中,我们有一些使用IUserProvider的简单类,它使用构造函数注入注入:

public class UserServiceSimple : IUserService
{
    public IUserProvider UserProvider { get; set; }
    public UserServiceSimple(IUserProvider userProvider)
    {
        UserProvider = userProvider;
    }
    public void CreateUser(User user)
    {
        UserProvider.CreateUser(user);
    }
}

现在我们可能有几个这样的类,并且在任何地方使用构造函数注入,但是在应用程序启动的主类中,所有这些类型都必须得到解决,因此我们必须使用服务定位器来解析所有这些类型,例如,在这里我将创建一个单件服务定位器类来解决控制台应用程序启动时的所有依赖关系,如下所示:

public class ServiceLocator
    {
        private readonly UnityContainer _container;

        private static ServiceLocator _instance;

        public static ServiceLocator Instance()
        {
            if (_instance == null)
            {
                _instance = new ServiceLocator();
                return _instance;
            }
            return _instance;
        }

        private ServiceLocator()
        {
            _container = new UnityContainer();
            _container.RegisterType<IUserProvider, UserProviderSimple>();
            _container.RegisterType<IUserService, UserServiceSimple>();
        }

        public T Resolve<T>()
        {
            return _container.Resolve<T>();
        }
    }
    class Program
    {
        private static IUserService _userService;
        private static void ConfigureDependencies()
        {
            _userService = ServiceLocator.Instance().Resolve<IUserService();
        }

        static void Main(string[] args)
        {
            ConfigureDependencies();
        }
    }

因此,似乎某种服务定位器总是在应用程序的开头使用,因此使用服务定位器是不可避免的,并且总是将其称为反模式权利是不正确的(除非它&#39) ; s不在应用程序的根目录中使用)?

4 个答案:

答案 0 :(得分:5)

您误解了服务定位器是什么。你确实理解了it is an anti-pattern这个好的部分,但是你所缺少的部分是应用程序中的模式是not about the mechanics, but the role it plays。换句话说:

  

封装在Composition Root中的DI容器不是服务定位器 - 它是基础架构组件。

答案 1 :(得分:3)

调用封装DI容器引导代码ServiceLocator的类没有任何内在错误,但您也可以将其称为StartupBootstrapContainerWrapper,它只是一个命名惯例。

另一方面,ServiceLocator作为一种设计模式通常被认为是反模式,因为它变成了对其余代码的硬依赖,并使更改和测试变得困难和不可预测。在您的代码中,您需要远离Resolve<T>方法以避免后果。

https://en.m.wikipedia.org/wiki/Service_locator_pattern

为了回答你的问题,通常需要一段代码来初始化DI容器,即使它是作为更大的DI框架本身的一部分而隐藏的,一些框架虽然允许从配置中配置容器文件也是。希望它有所帮助!

答案 2 :(得分:1)

很长一段时间以来,我一直在努力解决同样的问题。我可以提供你通常不需要ServiceLocator的体验(顺便说一句:这种反模式的最佳描述here以及你可以做些什么来避免它在相应的,非常棒的book中)。

请在下面重构您的代码。这里的基本思想是,您只有一个充当composition rootProgram)的根对象,并且容器会自动解析该根下面的复杂对象图的所有子依赖关系。

public class Bootstrapper
{
    private readonly UnityContainer _container;

    private Bootstrapper()
    {
        _container = new UnityContainer();
    }

    public Program Intialize()
    {
        this.ConfigureDependencies(UnityContainer container);
        return this.GetCompositionRoot();
    }

    private void ConfigureDependencies()
    {
        _container.RegisterType<IUserProvider, UserProviderSimple>();
        _container.RegisterType<IUserService, UserServiceSimple>();
        _container.RegisterType<Program, Program>();
    }

    private Program GetCompositionRoot()
    {
        return _container.Resolve<Program>();
    }
}

public class Program
{
    public Program(IUserService userService)
    {
        _userService = userService ?? throw AgrumentNullExcpetion(nameof(userService));
    }

    static void Main(string[] args)
    {
        var program = new Bootstrapper().Initialize();
        program.Run();
    }

    public void Run()
    {
        // Do your work using the injected dependency _userService
        // and return (exit) when done.
    }
}

答案 3 :(得分:0)

在某些情况下,它不适合,然后是,这是一种反模式。

我们必须查看模式是否有效,对于Service Locator,有几个用例。

在典型的业务应用程序中,您应该避免使用服务。它应该是没有其他选项时使用的模式。

例如,没有服务定位,控制容器的反转就无法工作。这是他们内部解决服务的方式。