我想生成一个文本输出列表,它遍历我的类或类列表的构造函数依赖项。我假设我会以某种方式使用反射来做到这一点?并且可以防止循环依赖。
https://stackoverflow.com/a/29704045/254257这似乎是我想要的,但他们没有提供任何代码。这个问题在类似的轨道上,但他们只是假设你已经开始使用一个字典,你的依赖关系已经被概括为字符串。所以我想我该如何开始。
说我有以下内容:
public class UserService(IGroupService groupService, ILoggingService loggingService)
public class GroupService(IUserService userService, IRoleService roleService, ILoggingService loggingService)
public class RoleService(ILoggingService loggingService)
我想要一些代码输出这样的东西:
UserService
---- GroupService
-------- UserService CIRCULAR DEPENDENCY (停止深入)
-------- RoleService
------------ LoggingService
-------- LoggingService
---- LoggingService
如果我想仅检查UserService的依赖关系,请使用接口的实际具体实现。
我知道我可以var type = typeof(UserService)
作为起点,但我之前只使用过属性,因此不确定下一步该怎么做。
我想我会以某种方式需要获取构造函数参数,那些类型,然后得到实际的实现并重复,以某种方式还确保我不会陷入循环,如果我有任何循环依赖。不知道如何做到这一点,所以将不胜感激。
答案 0 :(得分:1)
您遇到的问题源于您正在使用DI容器。使用Pure DI时出现这种问题的可能性要小得多,因为在这种情况下,C#编译器将验证对象构造,实际上不可能获得这种循环依赖。
使用DI容器时,请确保使用允许检测循环依赖关系并传达有意义错误的DI容器。事实上,任何成熟的DI容器都会非常清楚地传达循环依赖性错误。如果您选择的DI容器确实抛出了堆栈溢出异常,请考虑切换到成熟的容器。
例如,Simple Injector将使用以下消息抛出异常:
配置无效。为IGroupService类型创建实例失败。 GroupService类型直接或间接取决于它自身。循环图包含以下类型:GroupService - > UserService - > GroupService。
换句话说,Simple Injector显示循环图如下:
GroupService - > UserService - > GroupService
因此,您不需要对象图进行可视化,事实上,由于循环依赖性,大多数容器都无法执行此操作。如果您的对象图是非循环的,当您使用Visual Studio调试器钻入容器时,Simple Injector将按如下方式显示图形:
或者您可以通过编程方式使用Simple Injector的API来实现相同目的:
var graph = container.GetRegistration(typeof(UserService)).VisualizeObjectGraph();
结果如下:
UserService(
GroupService(
RoleService(
LoggingService()),
LoggingService()),
LoggingService())
请注意,您的里程可能因其他DI容器而异,但同样,大多数较成熟的库都包含这些类型的功能。
答案 1 :(得分:0)
好吧,它需要一些搞清楚,它可能并不完美,但对于我的代码它有效。我从Chetan的评论开始,然后走下了兔子洞。我把它变成了一个实用程序:
public static class DependencyChainUtil
{
public static TypeModel GetDependencyChainForType(Type type)
{
var currentChainClassList = new List<string>();
var model = GetDependencyChainForType(type, currentChainClassList);
return model;
}
private static TypeModel GetDependencyChainForType(Type type, List<string> currentChainClassList)
{
if (type != null)
{
var model = new TypeModel() {Type = type};
if (currentChainClassList.Any(x => x == type.FullName))
{
model.IsCircularReference = true;
}
else
{
currentChainClassList.Add(type.FullName);
var constructorInfo = type.GetConstructors().Where(x => x.GetParameters().Length > 0);
foreach (var info in constructorInfo)
{
foreach (var parameterInfo in info.GetParameters())
{
var subType = parameterInfo.ParameterType;
if (subType.IsInterface)
{
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes()).Where(x => x.GetInterfaces().Contains(subType))
.ToList();
if (types.Any())
{
subType = types.FirstOrDefault();
}
}
model.ConstructorDependencies.Add(GetDependencyChainForType(subType, currentChainClassList));
}
}
currentChainClassList.Remove(type.FullName);
}
return model;
}
throw new ArgumentNullException("Parameter 'type' is null.");
}
public static string OutputTextOfDependencyChain(TypeModel model)
{
var output = "";
var depth = 0;
if (model != null)
{
output = OutputTextOfDependencyChain(model, output, depth);
}
return output;
}
private static string OutputTextOfDependencyChain(TypeModel model, string output, int depth)
{
//prepend depth markers
output += new String(Enumerable.Range(0, depth*4).SelectMany(x => "-").ToArray());
output += model.Type.Name;
output += model.IsCircularReference ? "(CYCLIC DEPENDENCY)" : null;
output += "<br/>";
depth++;
foreach (var typeModel in model.ConstructorDependencies)
{
output = OutputTextOfDependencyChain(typeModel, output, depth);
}
return output;
}
}
public class TypeModel
{
public Type Type { get; set; }
public List<TypeModel> ConstructorDependencies { get; set; }
public bool IsCircularReference { get; set; }
public TypeModel()
{
ConstructorDependencies = new List<TypeModel>();
}
}