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

Is it possible to implement dependency injection without using service locator at the start of an application?

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

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

然后在业务逻辑层中,我们有一些简单的 class,它使用 IUserProvider,使用构造函数注入注入:

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

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

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();
        }
    }

所以似乎总是在应用程序开始时使用某种服务定位器,因此使用服务定位器是不可避免的,并且总是称其为反模式是不正确的(除非它不在应用程序中使用)应用程序的根)?

你误解了什么是服务定位器。您确实了解应用程序中 it is an anti-pattern, which is good, but what you're missing is that the pattern is not about the mechanics, but the role it plays 的部分。换句话说:

A DI container encapsulated in a Composition Root is not a Service Locator - it's an infrastructure component.

调用 class 封装 DI 容器引导代码 ServiceLocator 本质上没有错,但您也可以将其称为 StartupBootstrapContainerWrapper, 这只是一个命名约定。

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

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

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

有些情况下它不适合,那么是的,它是一个反模式。

我们必须查看是否存在模式的有效用法,对于服务定位器,有几个用例。

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

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

我已经为同一个问题苦苦挣扎了很长一段时间。我的经验是您通常不需要 ServiceLocator(顺便说一句:此反模式的最佳描述 here and what you can do to avoid it in the corresponding, very awsome, book)。

请查看下面的代码重构。这里的基本思想是,您只有一个根对象充当 composition root (Program),并且该根下面的复杂对象图的所有子依赖项都由容器自动解析。

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.
    }
}