我正在使用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断开连接。
当服务之间存在一些循环引用时,我得到同样的错误消息,我会在以后再次遇到这样的问题时添加一个示例和堆栈跟踪。
答案 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));
}
<强>用法:强>