简体   繁体   English

当接口具有其他接口的签名时,将DI与ninject一起使用

[英]Using DI with ninject when interface has signature of other interfaces

I have an interface called IXmlStrategy which has declared another Interface like this: 我有一个名为IXmlStrategy的接口,它已经声明了另一个接口,如下所示:

namespace IceServices.Business.Interfaces.Strategies
{
    public interface IXmlStrategy
    {
        IGetPersonNameXmlStrategy GetPersonNameXmlStrategy { get; }
    }
}

I expect there to be many strategy interfaces and I want to put all of them in once interface so I don't have to inject 10-20 IXmlStrategy variants. 我希望会有许多策略接口,并且我想将所有接口都放在一个接口中,因此不必注入10-20个IXmlStrategy变体。

This is my class using DI: 这是我使用DI的课程:

using System.Linq;
using IceServices.Business.Interfaces.Commands;
using IceServices.Business.Interfaces.Configurations;
using IceServices.Business.Interfaces.Deserializers;
using IceServices.Business.Interfaces.Mappers;
using IceServices.Business.Interfaces.Services;
using IceServices.Business.Interfaces.Strategies;
using IceServices.DomainObjects.ResponseObjects;

namespace IceServices.Business.Commands
{
    public class IpdCommands : IIpdCommands
    {
        private readonly ISoapService _soapService;
        private readonly IUrlConfigurations _urlConfigurations;
        private readonly IXmlDeserializer _xmlDeserializer;
        private readonly IDomainObjectMapper _domainObjectMapper;
        private readonly IXmlStrategy _xmlStrategy;

        public IpdCommands(ISoapService soapService, IUrlConfigurations urlConfigurations, IXmlDeserializer xmlDeserializer, IDomainObjectMapper domainObjectMapper, IXmlStrategy xmlStrategy)
        {
            _soapService = soapService;
            _urlConfigurations = urlConfigurations;
            _xmlDeserializer = xmlDeserializer;
            _domainObjectMapper = domainObjectMapper;
            _xmlStrategy = xmlStrategy;
        }

        public PersonNameResponse GetPersonName(int id)
        {
            var webServiceXmlString = _xmlStrategy.GetPersonNameXmlStrategy.GetXml(id.ToString());
            var listOfProperties = typeof(PersonNameResponse).GetProperties().Select(expr => expr.Name);

            var xmlStringResponse = _soapService.CallWebService(_urlConfigurations.GetPersonFirstname, webServiceXmlString);
            var stringArrayOfProperties = listOfProperties as string[] ?? listOfProperties.ToArray();
            var dictionaryData = _xmlDeserializer.DeserializeXmlToDictionary(stringArrayOfProperties, xmlStringResponse);
            return _domainObjectMapper.MapDictionaryToObject<PersonNameResponse>(dictionaryData, stringArrayOfProperties);
        }
    }
}

as you can see I'm calling GetPersonNameXmlStrategy.GetXml(id) like this: 如您所见,我正在像这样调用GetPersonNameXmlStrategy.GetXml(id):

  var webServiceXmlString = _xmlStrategy.GetPersonNameXmlStrategy.GetXml(id.ToString());

However I get a runtime error because IOC doesn't understand how to resolve it's dependency: 但是我收到一个运行时错误,因为IOC不了解如何解决它的依赖关系:

{"Message":"An error has occurred.","ExceptionMessage":"An error occurred when trying to create a controller of type 'IpdController'. Make sure that the controller has a parameterless public constructor.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\\r\\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\\r\\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()","InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Error activating IXmlStrategy using binding from IXmlStrategy to XmlStrategy\\r\\nNo constructor was available to create an instance of the implementation type.\\r\\n\\r\\nActivation path:\\r\\n 3) Injection of dependency IXmlStrategy into parameter xmlStrategy of constructor of type IpdCommands\\r\\n 2) Injection of de {“ Message”:“发生错误。”,“ ExceptionMessage”:“尝试创建类型为'IpdController'的控制器时发生错误。请确保该控制器具有无参数的公共构造函数。”,“ ExceptionType”: System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create上的“ System.InvalidOperationException”,“ StackTrace”:“(System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(httpRequestMessage request,HttpControllerDescriptor controllerDescriptor,type controllerType)\\ r \\ n HttpRequestMessage请求)\\ r \\ n位于System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()“,” InnerException“:{” Message“:”发生了错误。“,” ExceptionMessage“:”激活IXmlStrategy时出错使用从IXmlStrategy到XmlStrategy的绑定\\ r \\ n没有构造函数可用于创建实现类型的实例。\\ r \\ n \\ r \\ n激活路径:\\ r \\ n 3)将依赖项IXmlStrategy注入参数ipStrategy类型的构造函数的xmlStrategy \\ r \\ n 2)注入de pendency IIpdCommands into parameter ipdCommands of constructor of type IpdController\\r\\n 1) Request for IpdController\\r\\n\\r\\nSuggestions:\\r\\n 1) Ensure that the implementation type has a public constructor.\\r\\n 2) If you have implemented the Singleton pattern, use a binding with InSingletonScope() instead.\\r\\n","ExceptionType":"Ninject.ActivationException","StackTrace":" at Ninject.Activation.Providers.StandardProvider.Create(IContext context)\\r\\n at Ninject.Activation.Context.ResolveInternal(Object scope)\\r\\n at Ninject.Activation.Context.Resolve()\\r\\n at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\\r\\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable 1 source)\\r\\n at System.Linq.Enumerable.WhereSelectArrayIterator 2.MoveNext()\\r\\n at System.Linq.Buffer 1..ctor(IEnumerable 1 source)\\r\\n at System.Linq.Enumerable.ToArray[TSource](IEnumerable 1 source)\\r\\n at Ninject.Activation.Providers.StandardProvider.Create(IContext context)\\r\\n at Ninject.Activat 将ImpdCommands插入IpdController类型的构造函数的参数ipdCommands中\\ r \\ n 1)请求IpdController \\ r \\ n \\ r \\ n建议:\\ r \\ n 1)确保实现类型具有公共构造函数。\\ r \\ n 2)如果您已经实现了Singleton模式,请改为使用InSingletonScope()进行绑定。\\ r \\ n“,” ExceptionType“:” Ninject.ActivationException“,” StackTrace“:”在Ninject.Activation.Providers.StandardProvider.Create(IContext) context)\\ r \\ n在Ninject.Activation.Context.ResolveInternal(对象范围)\\ r \\ n在Ninject.Activation.Context.Resolve()\\ r \\ n在System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\\r\\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable 1源)\\ r \\ n,位于System.Linq.Enumerable.WhereSelectArrayIterator 2.MoveNext()\\r\\n at System.Linq.Buffer 1..ctor (IEnumerable 1 source)\\r\\n at System.Linq.Enumerable.ToArray[TSource](IEnumerable 1源)\\ r \\ n在Ninject.Activation.Providers.StandardProvider.Create(IContext上下文)\\ r \\ n在Ninject。激活 ion.Context.ResolveInternal(Object scope)\\r\\n at Ninject.Activation.Context.Resolve()\\r\\n at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\\r\\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable 1 source)\\r\\n at System.Linq.Enumerable.WhereSelectArrayIterator 2.MoveNext()\\r\\n at System.Linq.Buffer 1..ctor(IEnumerable 1 source)\\r\\n at System.Linq.Enumerable.ToArray[TSource](IEnumerable 1 source)\\r\\n at Ninject.Activation.Providers.StandardProvider.Create(IContext context)\\r\\n at Ninject.Activation.Context.ResolveInternal(Object scope)\\r\\n at Ninject.Activation.Context.Resolve()\\r\\n at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\\r\\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable 1 source)\\r\\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\\r\\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpReq ion.Context.ResolveInternal(对象作用域)\\ r \\ n在Ninject.Activation.Context.Resolve()\\ r \\ n在System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\\r\\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable 1源)\\ r \\ n在System.Linq.Enumerable.WhereSelectArrayIterator 2.MoveNext()\\r\\n at System.Linq.Buffer 1. 2.MoveNext()\\r\\n at System.Linq.Buffer 。ctor(IEnumerable 1 source)\\r\\n at System.Linq.Enumerable.ToArray[TSource](IEnumerable 1源)\\ r \\ n在Ninject.Activation.Providers.StandardProvider.Create(IContext上下文)\\ r \\ n在Ninject.Activation.Context.ResolveInternal(对象范围) \\ r \\ n在Ninject.Activation.Context.Resolve()\\ r \\ n在System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\\r\\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable 1 source )\\ r \\ n在System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage请求,类型controllerType,Func`1&激活器)\\ r \\ n在System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpReq uestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"}} uestMessage请求,HttpControllerDescriptor controllerDescriptor,类型controllerType)“}}

This is my IOC setup in Ninject.WebCommon: 这是我在Ninject.WebCommon中的IOC设置:

/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IIpdCommands>().To<IpdCommands>();
    kernel.Bind<IUrlConfigurations>().To<UrlConfigurations>();
    kernel.Bind<IXmlDeserializer>().To<XmlDeserializer>();
    kernel.Bind<IXmlDocumentFactory>().To<XmlDocumentFactory>();
    kernel.Bind<IDomainObjectMapper>().To<DomainObjectMapper>();
    kernel.Bind<ISoapService>().To<SoapService>();

    kernel.Bind<IXmlStrategy>().To<XmlStrategy>();
    kernel.Bind<IGetPersonNameXmlStrategy>().To<GetPersonNameXmlStrategy>();
}  

I need to change how the binding works for IGetPersonNameXmlStrategy but I'm not sure how. 我需要更改IGetPersonNameXmlStrategy绑定的工作方式,但不确定如何。 Does anyone know how I can get Ninject to resolve the depedency through IXmlStrategy.IGetPersonNameXmlStrategy ? 有谁知道我怎样才能通过IXmlStrategy.IGetPersonNameXmlStrategy来解决Ninject问题?

I expect there to be many strategy interfaces and I want to put all of them in once interface so I don't have to inject 10-20 IXmlStrategy variants. 我希望会有许多策略接口,并且我想将所有接口都放在一个接口中,因此不必注入10-20个IXmlStrategy变体。

Don't do this. 不要这样 What you are doing is described as a code smell here : 您在做什么在这里被描述为代码气味:

Service abstractions should not expose other service abstractions in their definition 服务抽象不应在其定义中公开其他服务抽象

In your case, your IXmlStrategy service abstraction exposes the IGetPersonNameXmlStrategy abstraction from its definition. 对于您的情况,您的IXmlStrategy服务抽象从其定义中公开IGetPersonNameXmlStrategy抽象。

put all of them in once interface so I don't have to inject 10-20 IXmlStrategy variants. 将它们全部放入一个接口中,这样我就不必注入10-20个IXmlStrategy变体。

This is a Interface Segregation Principle (ISP) violation. 这是违反接口隔离原则 (ISP)的行为。 ISP states that: ISP指出:

no client should be forced to depend on methods it does not use. 不应强迫任何客户端依赖其不使用的方法。

Having such wide interface will cause a few things: 具有如此宽的界面将导致以下几点:

  • The interface will keep growing, which forces you to update the existing implementations as well. 该界面将保持增长,这迫使您也必须更新现有的实现。
  • It complicates unit testing, because it becomes unclear what dependencies a class under test uses. 它使单元测试复杂化,因为不清楚测试中的类使用什么依赖项。
  • When you keep adding services to that interface, the interface becomes a form of a Service Locator . 当您继续向该接口添加服务时,该接口将成为Service Locator的一种形式。

Instead you should simply let a consumer depend on the service that it requires. 相反,您应该简单地让使用者依赖于它所需要的服务。 Not on the abstraction over the abstraction. 不在抽象之上。 In your specific case, inject the IGetPersonNameXmlStrategy directly into the consumer that requires it. 在您的特定情况下,将IGetPersonNameXmlStrategy直接注入需要它的使用者。

The most likely reason for you to have this Service Locator-like abstraction is because you are violating the Single Responsibility Principle (SRP). 您具有类似服务定位符的抽象的最可能原因是因为您违反了单一职责原则 (SRP)。 This principle states that classes should be focussed on one particular job, and only one requirement should cause them to change. 这项原则规定,班级应专注于一项特定的工作,只有一项要求才能使他们发生变化。 When a class has many dependencies, it is a good indication that SRP is violated. 当一个类具有许多依赖关系时,就很好地表明违反了SRP。

Classes that violate SRP tend to have constructors with many dependencies. 违反SRP的类倾向于具有许多依赖项的构造函数。 This is called constructor over-injection. 这称为构造函数过度注入。 Constructor over-injection however is a symptom. 但是,构造函数过度注入是一种症状。 You can fix over-injection by grouping dependencies, but that won't solve the root cause. 您可以通过对依赖项进行分组来解决注入过多问题,但这不能解决根本原因。 The root problem is that the class is too complex and grouping together those dependencies won't fix this problem. 根本问题是该类过于复杂,将这些依赖项组合在一起无法解决该问题。

Instead a refactoring should take place. 相反,应该进行重构。 For instance, you can extract all the code that you have in a single method into a class of its own. 例如,您可以将单个方法中拥有的所有代码提取到其自己的类中。 This leaves you with just one dependency that can be injected in the original class. 这样就只剩下一种可以注入到原始类中的依赖项。 When you do this with all methods, you might even be able to remove the original class altogether, because it might now have lost its meaning. 当使用所有方法执行此操作时,您甚至可以完全删除原始类,因为它现在可能已失去其含义。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM