简体   繁体   English

服务定位器和依赖注入

[英]Service locator and dependency injection

I think it's universally agreed that following is bad 我认为普遍认为以下是不好的

public class Foo
{
    private IService _service;
    public Foo()
    {
        _service = IocContainer.Resolve<IService>();
    }
}

and that following is preferred (Dependency Injection) 以下是首选(依赖注入)

public class Foo
{
    private IService _service;
    public Foo(IService service)
    {
    }
}

However now it's up to the consumer to provide the service. 但是现在由消费者提供服务。 The consumer could of course require the IService in the constructor as well, but it seems to be annoying when the hierarchy becomes deeper. 消费者当然也可以在构造函数中要求IService,但是当层次结构变得更深时,它似乎很烦人。 At some point someone needs to request the IService from the IoC container - but when...? 在某些时候,有人需要从IoC容器请求IService - 但是什么时候......?

A former colleague at my workplace had written a UnitOfWork class for the UoW/Repository pattern like this (using Microsoft ServiceLocator): 我工作场所的一位前同事为UoW / Repository模式编写了一个UnitOfWork类(使用Microsoft ServiceLocator):

public static UnitOfWork
{
    public static IUnitOfWork Current
    {
        get { return ServiceLocator.Current.GetInstance<IUnitOfWork>(); }
    }

    public static void Commit()
    {
        Current.Commit();
    }

    public static void Dispose()
    {
        Current.Dispose();
    }

    public static IRepository<T> GetRepository<T>() where T : class
    {
        return ServiceLocator.Current.GetInstance<IRepository>();
    }
}

and hooked up the IoC using Ninject so a request for IRepository would find the current UoW or create a new if required (if current is disposed). 并使用Ninject连接IoC,因此对IRepository的请求将找到当前的UoW或者在需要时创建一个新的(如果当前被处置)。 The usage becomes 使用成为

public class MyController
{    
    public void RunTasks()
    {
        var rep = UnitOfWork.GetRepository<Tasks>();
        var newTasks = from t in rep.GetAll()
                       where t.IsCompleted == false
                       select t;

        foreach (var task in newTasks)
        {
            // Do something
        }

        UnitOfWork.Commit();
    }
}

It does however still suffer from static IoC (service locator) class, but would there be a smarter solution? 但它确实仍然受到静态IoC(服务定位器)类的影响,但是会有更智能的解决方案吗? In this case there is no need to know about the internal dependencies (the static class has no logic), and for testing purposes an alternate IoC configuration can set up everything with mocks - and it's easy to use. 在这种情况下,不需要知道内部依赖性(静态类没有逻辑),并且出于测试目的,备用IoC配置可以使用模拟设置所有内容 - 并且它易于使用。

EDIT: 编辑:

I will try and clarify my confusion with a different example. 我将试着通过一个不同的例子澄清我的困惑。 Suppose I have a standard winforms application with a MainWindow class. 假设我有一个带有MainWindow类的标准winforms应用程序。 When the user click a button, I need to load some data from a database, and pass it to a class which will process the data: 当用户单击按钮时,我需要从数据库加载一些数据,并将其传递给将处理数据的类:

public class MainWindow : Form
{
    public MainWindow()
    {
    }

    private void OnUserCalculateClick(object sender, EventArgs args)
    {
        // Get UoW to connect to DB
        // Get instance of processor
    }
}

How would I get the instance of the processor and the unit of work? 我如何获得处理器的实例和工作单元? Can it be injected into a forms class? 它可以注入表格类吗?

I guess my question boils down to: If I am in a class which has been constructed without Ioc it could be a winform, a ria service class etc. - is it ok to refer to service locator/IoC controller to resolve instances of dependencies, or is there a preferred way of handling these cases? 我想我的问题归结为:如果我在一个没有Ioc构建的类中,它可能是一个winform,一个ria服务类等。 - 可以引用服务定位器/ IoC控制器来解析依赖关系的实例,或者是否有处理这些案件的首选方式? Or am I just doing something wrong...? 或者我只是做错了什么......?

About the first part of the question: 关于问题的第一部分:

The consumer could of course require the IService in the constructor as well, but it seems to be annoying when the hierarchy becomes deeper. 消费者当然也可以在构造函数中要求IService,但是当层次结构变得更深时,它似乎很烦人。

No, the consumer does not require an IService , it requires an IFoo . 不,消费者不需要IService ,它需要一个IFoo It does not know that the IFoo it will get has a dependency on IService , only your DI configuration knows that. 它不知道它将获得的IFoo依赖于IService ,只有你的DI配置才知道。 So, don't worry, you will not end up with this dependency hierarchy hell you describe . 所以,不用担心, 你不会最终得到你所描述的依赖层次结构

At some point someone needs to request the IService from the IoC container - but when...? 在某些时候,有人需要从IoC容器请求IService - 但是什么时候......?

That will happen only in your composition root . 这只会在你的作文根中发生。 So if it's an MVC application, you have somehow configured the MVC framework to use your DI configuration when it needs to instantiate controllers, so internally the framework decides (from routing) that it needs a MyController , and it does something like resolver.Get<MyController>() . 因此,如果它是一个MVC应用程序,你需要以某种方式配置MVC框架以在需要实例化控制器时使用你的DI配置,因此框架内部决定(从路由)它需要一个MyController ,它执行类似resolver.Get<MyController>() So service location is used only up there, not in your controllers or anywhere else. 因此,服务位置仅在那里使用,而不是在您的控制器或其他任何地方使用。

About the MyController part of the question: 关于MyController部分问题:

Can't really get the connection with the previous part, but still you can use constructor injection. 无法真正获得与前一部分的连接,但仍然可以使用构造函数注入。 No static classes (that are not injected, so cannot be swapped or mocked out for testing purposes), no service location. 没有静态类(未注入,因此无法交换或模拟用于测试目的),没有服务位置。

[As a side note, you could even avoid the extra code about unit of work (probably the ORM you use has one and you already using it at the implementation of your IRepositories ). [作为旁注,你甚至可以避免关于工作单元的额外代码(可能你使用的ORM有一个,你已经在IRepositories的实现中使用它)。 Maybe your repositories can have a SaveChanges method, which will call the unitOfWork's SaveChanges - but that's a matter of preference, and rather irrelevant with the previous discussion]. 也许你的存储库可以有一个SaveChanges方法,它将调用unitOfWork的SaveChanges - 但这是一个偏好问题,而且与之前的讨论无关。

The way I solved this is to have a UnitOfWorkFactory which has a Create method that creates your UnitOfWork . 我解决这个问题的方法是让UnitOfWorkFactory有一个Create方法来创建你的UnitOfWork

public interface IUnitOfWorkFactory
{
    IUnitOfWork Create();
}

public interface IUnitOfWork : IDisposable
{
    T GetRepository<T>();
    void Commit();
}

public class MyController
{    
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;

    public MyController(IUnitOfWorkFactory unitOfWorkFactory)
    {
        _unitOfWorkFactory = unitOfWorkFactory;
    }

    public void RunTasks()
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            var rep = UnitOfWork.GetRepository<Tasks>();
            var newTasks = from t in rep.GetAll()
                           where t.IsCompleted == false
                           select t;

            foreach (var task in newTasks)
            {
                // Do something
            }               

            unitOfWork.Commit();
        }
    }
}  

The advantage to having a factory is that it lets the user (the controller) control the creation and destruction of the unit-of-work. 拥有工厂的优势在于它允许用户(控制器)控制工作单元的创建和销毁。

This also makes unit-testing easier as you don't need to test using your IoC. 这也使得单元测试更容易,因为您不需要使用IoC进行测试。 I am also not a fan of having a global context available (like UnitOfWork.Current ) because it's difficult to determine when the UoW is going to be disposed or committed. 我也不是拥有全局上下文(如UnitOfWork.Current )的粉丝,因为很难确定何时处置或提交UoW。

If another class needs an instance of a UoW to add additional work to an existing context, you can pass in a specific instance. 如果另一个类需要UoW的实例来向现有上下文添加其他工作,则可以传入特定实例。

Using your first example, the container will construct both IFoo and IService . 使用您的第一个示例,容器将构造IFooIService Here is some real code to illustrate: 这是一些真实的代码来说明:

        container.RegisterType<ISubmittingService, GnipSubmittingService>(
            new DisposingTransientLifetimeManager(),
            new InjectionConstructor(
                typeof (IGnipHistoricConnection),
                typeof (IUserDataInterface),
                new EstimateVerboseLoggingService.TitleBuilder(),
                new EstimateVerboseLoggingService.FixedEndDateBuilder(),
                typeof (ISendEmailService),
                addresses,
                typeof (ILog)
                )
            );

        container.RegisterType<IEstimateService, EstimateVerboseLoggingService>(
            new DisposingTransientLifetimeManager(),
            new InjectionConstructor(
                typeof(IEstimateDataInterface),
                typeof(ISubmittingService),
                typeof(ILog)
                )
            );

... ...

    public EstimateVerboseLoggingService(
        IEstimateDataInterface estimateData,
        ISubmittingService submittingService,
        ILog log)
    {
        _estimateData = estimateData;
        _submittingService = submittingService;
        _log = log;
    }

... ...

    public GnipSubmittingService(
        IGnipHistoricConnection gnip,
        IUserDataInterface userDb,
        IBuilder<string, int> titleBuilder,
        IBuilder<DateTime, DateTime> endDateBuilder,
        ISendEmailService emailService,
        IEnumerable<MailAddress> errorEmailRecipients,
        ILog log)
    {
        _gnip = gnip;
        _userDb = userDb;
        _titleBuilder = titleBuilder;
        _endDateBuilder = endDateBuilder;
        _emailService = emailService;
        _errorEmailRecipients = errorEmailRecipients;
        _log = log;
    }

In this code, EstimateVerboseLoggingService consumes an ISubmitingService . 在此代码中, EstimateVerboseLoggingService使用ISubmitingService Both implementations are specified in the container. 两个实现都在容器中指定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从服务定位器到依赖注入 - Moving from Service Locator to Dependency Injection 没有服务定位器的多实例的依赖注入 - Dependency injection for multiple instance without service locator 服务定位器比依赖注入更容易使用? - Service Locator easier to use than dependency Injection? 使用哪种模式进行记录? 依赖注入或服务定位器? - Which pattern to use for logging? Dependency Injection or Service Locator? 使用依赖注入(Autofac)并避免服务定位器模式 - Using Dependency Injection (Autofac) and avoiding Service Locator pattern 创建易失性接口实例-依赖注入与服务定位器 - Creating volatile interfaces instances - dependency injection vs service locator 如何将此服务定位器模式转换为真正的依赖注入模式? - How to turn this Service Locator pattern into true Dependency Injection pattern? MVVM +服务+实体框架和依赖注入与服务定位器 - MVVM + Services + Entity Framework and Dependency Injection vs Service Locator 是否可以在应用程序启动时不使用服务定位器来实现依赖注入? - Is it possible to implement dependency injection without using service locator at the start of an application? Seemann的依赖注入,“三个呼叫模式”与服务定位器Anit-Pattern - Seemann's Dependency Injection, “Three Calls Pattern” vs Service Locator Anit-Pattern
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM