Wcf DI-过度使用还是糟糕的设计?

Wcf DI-过度使用还是糟糕的设计?,wcf,dependency-injection,Wcf,Dependency Injection,有两种情况下,队友们对如何使用DI不一致 我需要一个新的循环实例。我无法将作业实例传递给类ctor for(var item in items){ var job = new Job(); job.execute(item) } 一个选项是将IContainer注入类并使用container.Resolve()。但这是反模式的,容器是暴露的。另一个选项是注入工厂并将容器注入工厂。工厂方法最终将使用container.Resolve()这略有不同,但container仍然是公开的,并

有两种情况下,队友们对如何使用DI不一致

  • 我需要一个新的循环实例。我无法将作业实例传递给类ctor

    for(var item in items){
       var job = new Job();
       job.execute(item)
    }
    
    一个选项是将
    IContainer
    注入类并使用
    container.Resolve()
    。但这是反模式的,容器是暴露的。另一个选项是注入工厂并将容器注入工厂。工厂方法最终将使用
    container.Resolve()
    这略有不同,但container仍然是公开的,并且是反模式的

  • 注入类损坏的常见位置,例如WCF客户端,如果处于默认状态,我们需要新的客户端。我知道,大多数容器都能处理它,但在需要时我如何创建注入类的新实例呢。这是为了更好地控制。例如,如果WCF连接一旦创建并关闭或默认,类就不能重新创建客户端。例如,如果关闭或默认,Windsor将创建新的客户端实例

  • 我的类中的Once方法具有一次性类的依赖性。给班级注射一次性药物,对我来说似乎是个坏主意。因为它只在一个方法中使用,所以不管调用其他方法,仍然需要注入。如果依赖类被长时间使用,则无法处理它。我可以在方法本身中使用
    构造

  • 我的服务类依赖于太多的存储库,每一个存储库都在一个方法中使用。注入所有存储库对我来说没有意义,尽管这对性能几乎没有影响。我建议注入存储库工厂,但它将变成反模式,需要将容器注入工厂

  • 我的代码审查员(或我)有偏见,他不喜欢看到用于创建实例的新关键字。对他来说,因为我使用的是新的,所以它不是可测试的代码,我也不遵循DI。因此,我无法创建新的DTO,例如必须将
    DefaultPaymentOption
    注入
    Order
    实例。如果我将
    DefaultPaymentOption
    类注入
    Order
    ,我就失去了DTO的简单性,它也违反了DB数据<草稿模式下的订单不需要代码>默认付款选项
    。我们做得太过分了吗?在我的理解中,DI应该用于对象从外部世界执行一些操作,而不是用于合成

  • 我面临的另一个“非新”方法问题是,我必须向公众公开库的内部类(仅针对该库),以便从主机应用程序在引导程序中注册。例如,CustomJob库中的CustomJobExecuter(类型为IJobExecutor)需要在host app中注册,要注入它,我需要公开它。而我只能注册CustomJobManager并创建CustomJobExecutor的新实例并使用它


  • 在我看来,
    new
    关键字没有错。它是语言的一部分,所以它可以被使用,但与其他一切一样,你必须明智地使用它。DI的发明是为了使代码更易于测试、模块化和维护

    如果您的示例中的
    job
    没有需要容器(存储库、服务等)的依赖项,是可测试的,并且不会给您的依赖项引入新的周期,那么,imho,
    new
    完全可以。如果将来您需要容器中的依赖项,那么您将用
    container.get(…)
    替换
    new
    。和DTO一样,从容器中获取它们是一种过分的做法,因为它们应该没有逻辑和依赖关系


    而且你不必公开你的课程。1) 有些语言提供的可视范围比公共和私有范围更大(例如java—默认范围)。2) 您不必单独测试每个类——这称为单元测试,而不是类测试。但可悲的事实是:有时为了更简单的测试,您必须放松可见性

    我看到您的DI方法的一个指导问题来自于对它的用途的误解。当您试图将系统的实现细节抽象为通过单元测试单独测试的小类时,您希望使用DI。如果您创建了一个包含内部类的具体类,那么很可能您的类做了太多的工作,应该分解为经过单元测试的组成部分。这样,当某个代码出现故障时,您几乎可以立即知道问题出在哪里,这取决于代码的哪一部分出现故障

    我会更深入地阅读坚实的设计原则和单元测试,以更坚实地理解这些模式背后的目的

  • 在某一点上,你需要做一些实际的工作,为了完成这项工作,可以创建一个新的实例。例如,当与db2/AS400交互时,我们没有针对我们的数据服务层编写测试的选项,而数据服务层则通过更新连接实例来编写数据

  • 这取决于您的DI容器,将是一个单独的问题。我知道在SimpleInjector中,您可以执行RegisterPerRequest调用,该调用要求每次返回新实例

  • 一次性类是一个实现细节,您可以自由地新建一个实例,以便将实现细节限制在该特定类中。或者,您可以将其封装在一个层中,该层在完成所需的职责后处理处置逻辑

  • 听起来你的服务似乎成了上帝的目标,承担了太多的责任。这项服务的责任是什么

  • 查看automapper for DTO,因为它将允许您模拟IMappingEngine,并通过模拟对var DTO=mappingEngine.Map(model)的调用来启用单元测试

  • 是的,如果要测试内部类,您需要将它们公开

  • 当y
    public interface ISomeObject
    {
        void DoSomething();
    }
    
    public interface ISomeConfigurationDependency
    {
        // A dependency that is configured in the DI container.
    }
    
    public interface ISomeRuntimeDependency
    {
        // A dependency created by some other factory at runtime.
    }
    
    public interface ISomeObjectFactory
    {
        ISomeObject Create(string someRuntimeValue, ISomeRuntimeDependency someRuntimeDependency);
        void Release(ISomeObject someObject);
    }
    
    public class SomeObject : ISomeObject
    {
        private readonly string someRuntimeValue;
        private readonly string someConfigurationValue;
        private readonly ISomeRuntimeDependency someRuntimeDependency;
        private readonly ISomeConfigurationDependency someConfigurationDependency;
    
        public SomeObject(
            string someRuntimeValue, 
            string someConfigurationValue, 
            ISomeRuntimeDependency someRuntimeDependency, 
            ISomeConfigurationDependency someConfigurationDependency)
        {
            if (string.IsNullOrEmpty(someRuntimeValue))
                throw new ArgumentNullException("someRuntimeValue");
            if (string.IsNullOrEmpty(someConfigurationValue))
                throw new ArgumentNullException("someConfigurationValue");
            if (someRuntimeDependency == null)
                throw new ArgumentNullException("someRuntimeDependency");
            if (someConfigurationDependency == null)
                throw new ArgumentNullException("someConfigurationDependency");
    
            this.someRuntimeValue = someRuntimeValue;
            this.someConfigurationValue = someConfigurationValue;
            this.someRuntimeDependency = someRuntimeDependency;
            this.someConfigurationDependency = someConfigurationDependency;
        }
    
        public void DoSomething()
        {
            Console.WriteLine("I am using the dependencies now");
        }
    }
    
    
    public class SomeObjectFactory : ISomeObjectFactory
    {
        private readonly string someConfigurationValue;
        private readonly ISomeConfigurationDependency someConfigurationDependency;
    
        public SomeObjectFactory(string someConfigurationValue, ISomeConfigurationDependency someConfigurationDependency)
        {
            if (string.IsNullOrEmpty(someConfigurationValue))
                throw new ArgumentNullException("someConfigurationValue");
            if (someConfigurationDependency == null)
                throw new ArgumentNullException("someConfigurationDependency");
    
            this.someConfigurationValue = someConfigurationValue;
            this.someConfigurationDependency = someConfigurationDependency;
        }
    
        public ISomeObject Create(string someRuntimeValue, ISomeRuntimeDependency someRuntimeDependency)
        {
            if (string.IsNullOrEmpty(someRuntimeValue))
                throw new ArgumentNullException("someRuntimeValue");
            if (someRuntimeDependency == null)
                throw new ArgumentNullException("someRuntimeDependency");
    
            // The only thing a factory does is new up an instance - 
            // no business logic here. There is nothing to test  
            // here because nothing can go wrong.
            return new SomeObject(
                someRuntimeValue, 
                this.someConfigurationValue, 
                someRuntimeDependency, 
                this.someConfigurationDependency);
        }
    
        // Use release if you want to expose the factory as a formal extension point
        // that might have unmanaged dependencies.
        public void Release(ISomeObject someObject)
        {
            var disposable = someObject as IDisposable;
            if (disposable != null)
                disposable.Dispose();
        }
    }
    
    public class SomeService : ISomeService
    {
        private readonly ISomeObjectFactory someObjectFactory;
        private readonly ISomeRuntimeDependencyFactory someRuntimeDependencyFactory;
    
        public SomeService(ISomeObjectFactory someObjectFactory, ISomeRuntimeDependencyFactory someRuntimeDependencyFactory)
        {
            if (someObjectFactory == null)
                throw new ArgumentNullException("someObjectFactory");
            if (someRuntimeDependencyFactory == null)
                throw new ArgumentNullException("someRuntimeDependencyFactory");
    
            this.someObjectFactory = someObjectFactory;
            this.someRuntimeDependencyFactory = someRuntimeDependencyFactory;
        }
    
        public void Run()
        {
            // Setup runtime variables
            var someRuntimeValue = "test";
            var someRuntimeDependency = this.someRuntimeDependencyFactory.Create();
    
            // Get a runtime instance
            var someObject = this.someObjectFactory.Create(someRuntimeValue, someRuntimeDependency);
            try
            {
                someObject.DoSomething();
            }
            finally
            {
                this.someObjectFactory.Release(someObject);
            }
        }
    }