Как определить тип, вызывающий проблему Нет конструктора без параметров для этого объекта

Я работаю с NopCommerce, где для DI используется Autofac. Существует множество сервисов, и иногда могут возникать циклические зависимости, что приводит к ошибке:

Для этого объекта не определен конструктор без параметров.

Довольно сложно выяснить, какие службы имеют циклические зависимости, как определить тип, который не может быть создан из-за циклической зависимости или по другой причине?

  1. Пример ошибки,

CustomerController на Nop.Web.Controllers содержит множество сервисов:

регион 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 отключен.

То же самое сообщение об ошибке я получаю, когда между службами есть циклические ссылки, я добавлю пример и проследим стек позже, когда снова столкнусь с такой проблемой.

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

использование:

Другие вопросы по тегам