带有构造函数参数的子类的工厂依赖注入

时间:2014-05-09 11:02:54

标签: c# .net dependency-injection ninject ninject-extensions

我有这个使用Ninject for DI的应用程序,我有以下结构:

public class SomeServicHost
{
    private ISomeService service;

    public SomeServicHost(ISomeService service)
    {
        this.service = service;
    }

    public void DoSomething(int id)
    {
        // Injected instance
        this.service.DoWhatever("Whatever");

        // Without injection - this would be how it would be instantiated
        var s = new SomeService(new Repo(id));
        s.DoWhatever("Whatever");
    }
}

interface ISomeService
{
    void DoWhatever(string id);
}

class SomeService : ISomeService
{
    private IRepo SomeRepo;

    public SomeService(IRepo someRepo)
    {
        this.SomeRepo = someRepo;
    }

    public void DoWhatever(string id)
    {
        SomeRepo.DoSomething();
    }
}

interface IRepo
{
    void DoSomething();
}

class Repo : IRepo
{
    private int queueId;

    public Repo(int queueId)
    {
        this.queueId = queueId;
    }

    public void DoSomething()
    {
        // Whatever happens
    }

因此正常注射不起作用。我需要一个工厂。所以我可以这样做:

interface IServiceFactory
{
    ISomeService GetService(int id)
    {
        return new SomeService(new Repo(id));
    }
}

我已经熟悉了。但是,如果我这样做,我会失去所有的DI优点,比如生命周期管理,或者将一个实现换成另一个。有更优雅的方式吗?

2 个答案:

答案 0 :(得分:0)

而不是做

new SomeService(new Repo(id));

你可以做到

IResolutionRoot.Get<Repo>(new ConstructorArgument("id", id));

IResolutionRoot是内核的类型解析接口,您可以注入构造函数。这将使您从“DI善良”中受益,正如您所说的那样。请注意,您可能还想使用ninject.extensions.contextpreservation扩展程序。

@Simon Whitehead已经指出的ninject.extensions.factory,可以帮助你消除锅炉板代码,基本上完全按照我的描述进行操作。

通过将IResolutionRoot.Get<>调用移动到另一个类(.. factory ...),您可以从实例中解除SomeService的负担。 ninject.extension.factory正是这样做的。由于扩展不需要实际实现,您甚至可以节省一些单元测试!那么,如果SomeService被扩展并且需要更多的依赖关系,那么DI管理呢?好吧不用担心,因为你使用内核来实现类,它会自动工作,IoC将按照它应该管理你的依赖项。 如果您不使用DI进行实例化,最终可能会在最后使用非常少的DI。

正如@ zafeiris.m所指出的,这称为服务定位器。并且通常也被称为反模式。这是正确的!只要你能够获得更好的解决方案,你应该。在这里你可能不会。可以说它更好,所以坚持使用最好的解决方案,无论人们怎么称呼它。

可能有办法减少服务地点的使用。例如,如果您有一个业务案例,如“UpdateOrder(id)”,该方法将为该ID创建一个服务,然后为该ID创建另一个服务,然后为该ID创建一个记录器,您可能想要更改它创建一个对象,该对象将ID作为可继承的构造函数参数,并将特定于ID的服务和记录器注入该对象,从而将3个工厂/服务定位器调用减少为1.

答案 1 :(得分:0)

按照你的描述保持工厂,对我来说似乎很好。这就是它们的用途,当您拥有只能在运行时根据某些运行时值实例化的服务时(例如您的id),工厂就可以了。

  

但如果我这样做,我会失去所有的DI善良,就像生命周期一样   管理,或将一个实施与另一个实施交换。

DI不是工厂的替代品。您需要两个世界,这将导致灵活,松散耦合的设计。工厂用于新建依赖关系,他们知道如何创建它们,使用什么配置以及何时处理它们,如果你想交换实现,你仍然只改变一个地方:只是这次它是工厂,而不是你的DI配置。

作为最后一点,你可能会试图使用另一个答案中提出的service locator anti-pattern。您最终会得到更糟糕的设计,更少可测试,更不清晰,可能与您的DI容器紧密耦合等等。您使用工厂的想法要好得多。 (here are更多示例,与您的类似。)