如何识别导致问题的类型没有为此对象定义无参数构造函数

时间:2015-07-27 18:34:50

标签: dependency-injection autofac nopcommerce

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

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

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

  1. 错误示例,
  2. 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连接,当我断开连接时会收到以下错误:

    enter image description here

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

    当服务之间存在一些循环引用时,我得到同样的错误消息,我会在以后再次遇到这样的问题时添加一个示例和堆栈跟踪。

1 个答案:

答案 0 :(得分:1)

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

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;
        }
    }

然后定义这样的动作方法:

    [HttpGet]
    public ActionResult TestCreateController(string controllerType)
    {
        return Content(ControllerActivatorHelper.TestCreateController(controllerType));
    }

<强>用法:

enter image description here