Как определить тип, вызывающий проблему Нет конструктора без параметров для этого объекта
Я работаю с NopCommerce, где для DI используется Autofac. Существует множество сервисов, и иногда могут возникать циклические зависимости, что приводит к ошибке:
Для этого объекта не определен конструктор без параметров.
Довольно сложно выяснить, какие службы имеют циклические зависимости, как определить тип, который не может быть создан из-за циклической зависимости или по другой причине?
- Пример ошибки,
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));
}
использование: