Dependency injection 如何识别导致此对象未定义无参数构造函数的问题的类型

Dependency injection 如何识别导致此对象未定义无参数构造函数的问题的类型,dependency-injection,autofac,nopcommerce,Dependency Injection,Autofac,Nopcommerce,我在NopCommerce工作,那里使用DI Autofac。有很多服务,有时可能会发生循环依赖,从而导致错误: 没有为此对象定义无参数构造函数。 很难找出哪些服务具有循环依赖,如何捕获由于循环依赖或其他原因而无法实例化的类型 错误示例 位于Nop.Web.Controllers的CustomerController注入了许多服务: 区域c-tor public CustomerController(IAuthenticationService authenticationServic

我在NopCommerce工作,那里使用DI Autofac。有很多服务,有时可能会发生循环依赖,从而导致错误:

没有为此对象定义无参数构造函数。

很难找出哪些服务具有循环依赖,如何捕获由于循环依赖或其他原因而无法实例化的类型

  • 错误示例
  • 位于Nop.Web.Controllers的CustomerController注入了许多服务:

    区域c-tor

         public CustomerController(IAuthenticationService authenticationService,
                IDateTimeHelper dateTimeHelper,
                DateTimeSettings dateTimeSettings, 
                TaxSettings taxSettings,
                ILocalizationService localizationService,
                IWorkContext workContext,
                IStoreContext storeContext,
                ICustomerService customerService,
                IGenericAttributeService genericAttributeService,
                ....
                etc.)
            {
                this._authenticationService = authenticationService;
                this._dateTimeHelper = dateTimeHelper;
                this._dateTimeSettings = dateTimeSettings;
                this._taxSettings = taxSettings;
                this._localizationService = localizationService;
                this._workContext = workContext;
                this._storeContext = storeContext;
                this._customerService = customerService;
                this._genericAttributeService = genericAttributeService;
                ....
                etc.
              }
    
    终端区域

    一些要初始化的服务,或者我不知道,需要VPN连接,当我断开连接时,会出现以下错误:

    这个错误说明不了什么,我可能会猜到哪个服务没有初始化,问题出在哪里,直到我注意到VPN断开连接


    当服务之间存在一些循环引用时,我会收到相同类型的错误消息,稍后当再次遇到此类问题时,我将添加一个示例和堆栈跟踪。

    我开发了一个帮助程序,可以识别无法解决的服务:

    public static class ControllerActivatorHelper
    {
        public static string TestCreateController(string controllerType)
        {
            try
            {
                var type = TryGetType(controllerType);
                if (type == null)
                    return "Can't find type " + controllerType;
                return TestCreateController(type);
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    
        private static Type TryGetType(string typeName)
        {
            var type = Type.GetType(typeName);
            if (type != null) return type;
            foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = a.GetType(typeName);
                if (type != null)
                    return type;
            }
            return null;
        }
    
        /// <summary>
        /// No Parameterless Constructor Error is quite difficult to indentify what service is missing in dependencies registration
        /// this is a helper method allows to see the log of resolving types for each c-tor argument
        /// </summary>
        public static string TestCreateController(Type controllerType)
        {
            StringBuilder log = new StringBuilder();
            DefaultControllerActivator activator = new DefaultControllerActivator();
            log.AppendFormat("<h2>Inspecting type '{0}'</h2>", controllerType.FullName);
            if (activator.CanCreate(controllerType) == false)
            {
                var ctors = controllerType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
                List<Type> inspectedTypes = new List<Type>();
                bool anyFail = false;
                foreach (var ctor in ctors)
                {
                    var parameters = ctor.GetParameters();
                    foreach (var parameterInfo in parameters)
                    {
                        try
                        {
                            if (!inspectedTypes.Contains(parameterInfo.ParameterType))
                            {
                                log.AppendLine("<br/>");
                                inspectedTypes.Add(parameterInfo.ParameterType);
                                log.AppendFormat("Resolving {0} {1}..", parameterInfo.ParameterType.Name,
                                    parameterInfo.Name);
                                var resolvedType = EngineContext.Current.Resolve(parameterInfo.ParameterType);
                                log.AppendFormat(" SUCCESS. Resolved type is '{0}'", resolvedType.GetType().FullName);
                            }
                        }
                        catch (Exception ex)
                        {
                            log.Append(" <strong>FAILED</strong>");
                            log.AppendLine("<p><strong>Error:</strong>" + ex.Message + "<p/>");
                            anyFail = true;
                        }
                    }
                }
                if (!anyFail)
                {
                    //inspect fields
                    var fields = controllerType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
                    foreach (var fieldInfo in fields)
                    {
                        try
                        {
                            if (!inspectedTypes.Contains(fieldInfo.FieldType))
                            {
                                log.AppendLine("<br/>");
                                inspectedTypes.Add(fieldInfo.FieldType);
                                log.AppendFormat("Resolving {0} {1}..", fieldInfo.FieldType.Name, fieldInfo.Name);
                                var resolvedType = EngineContext.Current.Resolve(fieldInfo.FieldType);
                                log.AppendFormat(" SUCCESS. <br/> [Resolved type is '{0}']",
                                    resolvedType.GetType().FullName);
                            }
                        }
                        catch (Exception ex)
                        {
                            log.Append(" <strong>FAILED</strong>");
                            log.AppendLine("<p><strong>Error:</strong>" + ex.Message + "<p/>");
                            anyFail = true;
                        }
                    }
                }
                if (!anyFail)
                {
                    log.AppendFormat("<h3>{0} c-tor arguments are Ok</h3>", controllerType.Name);
                    try
                    {
                        var resolvedCtor = EngineContext.Current.ContainerManager.Resolve(controllerType);
                    }
                    catch (Exception ex)
                    {
                        log.AppendFormat(
                            "<h3 style='color:red'>Check {0} c-tor body there should be some logic which crashes and makes imposible to create an instance of the controller!</h3>", controllerType.Name);
                        log.AppendFormat("<b>Error:</b> {0}", ex.Message);
                        var innerException = ex.InnerException;
                        while (innerException != null)
                        {
                            log.AppendFormat("<br/><b>InnerException: </b>{0} <p>StackTrace: {1}</p>",
                                innerException.Message, innerException.StackTrace);
                            innerException = innerException.InnerException;
                        }
                    }
                }
    
            }
            else
            {
                log.AppendFormat("'{0}' created successfully", controllerType.FullName);
            }
            return log.ToString();
        }
    
        class DefaultControllerActivator : IControllerActivator
        {
            private readonly Func<IDependencyResolver> _resolverThunk;
            public DefaultControllerActivator()
                : this(null)
            {
            }
            public DefaultControllerActivator(IDependencyResolver resolver)
            {
                if (resolver == null)
                {
                    this._resolverThunk = (() => DependencyResolver.Current);
                    return;
                }
                this._resolverThunk = (() => resolver);
            }
    
            public bool CanCreate(Type controllerType)
            {
                try
                {
                    var result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
                    return true;
                }
                catch (Exception innerException)
                {
                    return false;
                }
            }
    
    
            public IController Create(RequestContext requestContext, Type controllerType)
            {
                IController result;
                try
                {
                    result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
                }
                catch (Exception innerException)
                {
                    throw new InvalidOperationException(
                        string.Format("An error occurred when trying to create a controller of type '{0}'. Make sure that the controller has a parameterless public constructor.", controllerType), innerException);
                }
                return result;
            }
        }
    
    用法:


    此错误消息看起来不像Autofac错误消息,而是EntityFramework错误消息。你有堆栈跟踪吗?也许有一个工具会有帮助,但每当我遇到这个问题时,我就开始从构造函数中注释服务,然后重新运行,直到我识别出违规者为止。一旦发现需要进入该服务的构造函数以缩小其范围。
        [HttpGet]
        public ActionResult TestCreateController(string controllerType)
        {
            return Content(ControllerActivatorHelper.TestCreateController(controllerType));
        }