I would like to generate a text output list that traverses my constructor dependencies for a class or list of classes. I assume I would use reflection in some way to do this? And have protection against circular dependencies.
https://stackoverflow.com/a/29704045/254257 This seems to be what I would want, but they provided no code. That question is on a similar track, but they just assume you have start with a dictionary with your dependencies already outlined as strings. So I guess how would I get that to start with.
Say I have the following:
public class UserService(IGroupService groupService, ILoggingService loggingService)
public class GroupService(IUserService userService, IRoleService roleService, ILoggingService loggingService)
public class RoleService(ILoggingService loggingService)
I would want some code to output something like this:
UserService
----GroupService
--------UserService CIRCULAR DEPENDENCY (stops going any deeper)
--------RoleService
------------LoggingService
--------LoggingService
----LoggingService
If I wanted to check dependencies on only the UserService, with the actual concrete implementation of the interfaces.
I know I can var type = typeof(UserService)
as a starting point, but I've only ever worked with properties before so not sure what to do next.
I would imagine I would somehow need to get the constructor parameters, the types of those, then get the actual implementations and repeat, somehow also making sure I don't get stuck in a loop if I have any circular dependencies. Not sure how to do any of that so help would be appreciated.
Well it took some figuring out and it's probably not perfect, but for my code it worked. I started at Chetan's comment and just went down the rabbit hole. I made it a Utility:
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>();
}
}