如何为 class 或 class 列表生成构造函数依赖图?

How can I generate a constructor dependency graph for a class or list of classes?

我想生成一个文本输出列表,该列表遍历 class 或 classes 的构造函数依赖项。我假设我会以某种方式使用反射来做到这一点?并防止循环依赖。

这似乎是我想要的,但他们没有提供代码。这个问题在类似的轨道上,但他们只是假设你已经从一个字典开始,你的依赖项已经被概述为字符串。所以我想我该如何开始。

假设我有以下内容:

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 (stops going any deeper)

--------RoleService

------------LoggingService

--------LoggingService

----LoggingService

如果我只想检查 UserService 的依赖关系,以及接口的实际具体实现。

我知道我可以 var type = typeof(UserService) 作为起点,但我之前只使用过属性,所以不确定下一步该怎么做。

我想我会以某种方式需要获取构造函数参数,这些参数的类型,然后获取实际的实现并重复,如果我有任何循环依赖关系,还可以以某种方式确保我不会陷入循环.不确定该怎么做,我们将不胜感激。

您遇到的问题源于您使用的是 DI 容器。使用 Pure DI 时不太可能出现这种问题,因为在这种情况下,C# 编译器将验证对象构造,并且实际上不可能获得这种循环依赖。

当您使用 DI 容器时,请确保您使用的 DI 容器允许检测循环依赖并传达有意义的错误。事实上,任何成熟的 DI 容器都会非常清楚地传达循环依赖错误。如果您选择的 DI 容器确实抛出堆栈溢出异常,请考虑切换到成熟的容器。

例如,

Simple Injector 将抛出异常并显示以下消息:

The configuration is invalid. Creating the instance for type IGroupService failed. The type GroupService is directly or indirectly depending on itself. The cyclic graph contains the following types: 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 容器不同,但同样,大多数更成熟的库都包含这些类型的功能。

好吧,它花了一些时间才弄清楚,它可能并不完美,但对于我的代码来说它是有效的。我从 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>();
    }
}