C# 具有构造函数参数的子类的工厂依赖项注入

C# 具有构造函数参数的子类的工厂依赖项注入,c#,.net,dependency-injection,ninject,ninject-extensions,C#,.net,Dependency Injection,Ninject,Ninject Extensions,我有一个使用Ninject进行DI的应用程序,我有以下结构: public class SomeServicHost { private ISomeService service; public SomeServicHost(ISomeService service) { this.service = service; } public void DoSomething(int id) { // Injected

我有一个使用Ninject进行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的优点,比如生命周期管理,或者用一个实现替换另一个实现。有更优雅的方法吗?

而不是

new SomeService(new Repo(id));
你能行

IResolutionRoot.Get<Repo>(new ConstructorArgument("id", id));
IResolutionRoot是内核的类型解析接口,您可以将其注入构造函数。这将允许您从您所称的DI善中获益。请注意,您可能还希望使用ninject.extensions.contextpreservation扩展

@Simon Whitehead已经指出的ninject.extensions.factory可以帮助您消除锅炉板代码,基本上实现了我上面描述的功能

通过将IResolutionRoot.Get调用移动到另一个类。。工厂您从实例中释放了一些服务。ninject.extension.factory就是这样做的。由于扩展不需要实际的实现,您甚至可以为自己保存一些单元测试!此外,如果某个服务被扩展并需要DI管理的更多依赖项,该怎么办?不用担心,因为您正在使用内核来实例化类,它将自动工作,IoC将按其应有的方式管理您的依赖关系。 如果您不使用DI来进行实例化,那么最终可能只使用很少的DI

正如@zafeiris.m指出的,这被称为服务定位器。也常被称为反模式。没错!只要你能找到更好的解决方案,你就应该。在这里,你可能不能。只要说它更好就够了,所以不管人们怎么称呼它,都要坚持使用最好的解决方案


可能有一些方法可以减少服务位置的使用。例如,如果您有一个业务案例,如UpdateOrderid,那么该方法将为该ID创建一个服务,然后为该ID创建另一个服务,然后为该ID创建一个记录器,。。您可能希望将其更改为创建一个对象,该对象将ID作为可继承的构造函数参数,并将ID特定的服务和记录器注入该对象中,从而将3个工厂/服务定位器调用减少为1。

保持工厂与您描述的一样,对我来说似乎很好。这就是它们的用途,当您拥有的服务只能在运行时根据一些运行时值(如id)进行实例化时,工厂就是一种方法

但如果我这样做,我就失去了所有的DI优点,比如生命周期 管理,或者用一个实现替换另一个实现

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


最后请注意,您可能会尝试使用另一个答案中建议的。最终的结果是糟糕的设计,不易测试,不清晰,可能与DI容器紧密耦合,等等。你使用工厂的想法要好得多。更多与您类似的示例。

。您看过扩展吗?嗨,西蒙,是的,我看过。根据网站“此Ninject扩展允许自动创建工厂实现”。工厂不是问题所在。扩展创建的工厂完全参与Ninject生命周期管理/范围界定/等。如果工厂不是问题所在,那么我不清楚您的实际问题是什么?你能重新表述你的问题吗?这不是个好主意。这样一来,某个服务主机不仅要使用,而且还要承担新服务的额外责任,同时还要承担托管/使用该服务的真正责任。那么,如果某个服务有其他由IoC管理的依赖项,或者如果在以后某个时间它获得了更多的依赖项,该怎么办?他应该把所有的都换成新的吗?如果这些依赖项又有其他依赖项呢?我不认为新的ing模式比临时服务定位器更好,但这是有争议的,还取决于你改变IoC的频率。。。。ninject的另一个特点是它的工厂扩展,它可以帮助您非常简单地创建具有IoC支持的工厂。因此,对IoC的依赖性是最小的。@BatteryBackupUnit工厂可以使用构造函数注入来获取运行前已知的所有依赖性,并只更新需要一些运行时值的其余依赖性。例如,工厂方法可能类似于返回new SomeServicenew Repoid、\u otherService、\u otherRepo,并将其他依赖项注入到factorygood point,但像ninject scope这样的方法仍然不起作用。另外,t的单元测试 新的指导将是一个真正的麻烦。要么你的测试不够精确,要么你的设计比实际消费者需要的更大的类接口更糟糕。真的值得吗?一个人通常不会像更换内衣那样频繁。我仍然不相信必须解决这个可维护性级别。