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

时间:2018-05-16 15:22:28

标签: c# reflection dependency-injection system.reflection

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

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)作为起点,但我之前只使用过属性,因此不确定下一步该怎么做。

我想我会以某种方式需要获取构造函数参数,那些类型,然后得到实际的实现并重复,以某种方式还确保我不会陷入循环,如果我有任何循环依赖。不知道如何做到这一点,所以将不胜感激。

2 个答案:

答案 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将按如下方式显示图形:

enter image description here

或者您可以通过编程方式使用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>();
    }
}