Sort .NET nodes by dependency

My project has a set of .NET assemblies. I want to sort them by their dependencies.

So, if I have (for example):

IEnumerable<Assembly> unsorted = LoadAssembliesFromFolder(); 

I want to be able to call:

 var IEnumerable<Assembly> sorted = unsorted.SortByDependency(); 

In reality, the resulting set, we hope, will ultimately be similar to the dialog of the order of building a project in Visual Studio.

Any thoughts? I really do not want to dwell on an iterative approach, which may take some time.

Greetings

+4
source share
3 answers

You will need the levarage GetReferencedAssemblies() Assembly method. This returns a list of AssemblyName values ​​and allows you to load the next assembly. This is terribly inefficient and will ensure that each assembly is loaded into memory, but this is something: -D

 class AssemblyReferenceComparison : IComparer<Assembly> { public int Compare(Assembly x, Assembly y) { if (x == y) return 0; if (GetReferencesAssemblies(x).Contains(y)) return -1; if (GetReferencesAssemblies(y).Contains(x)) return 1; return 0; } private static IEnumerable<Assembly> GetReferencesAssemblies(Assembly a) { var referencedAssemblies = new HashSet<Assembly>(); FillReferencesAssemblies(a, referencedAssemblies); return referencedAssemblies; } private static void FillReferencesAssemblies(Assembly a, HashSet<Assembly> referencedAssemblies) { referencedAssemblies.Add(a); var directAssemblies = a.GetReferencedAssemblies() .Select(name => Load(name)) .Where(asm => asm != null) .Where(asm => !referencedAssemblies.Contains(asm)) .ToArray(); foreach (var directAssembly in directAssemblies) { FillReferencesAssemblies(directAssembly, referencedAssemblies); } } [DebuggerStepThrough] private static Assembly Load(AssemblyName name) { try { return Assembly.Load(name); } catch { return null; } } } 

For use:

 var assemblies = LoadAssembliesFromFolder() .OrderBy(a => a, new AssemblyReferenceComparison()) .ThenBy(a => a.FullName); 
+3
source

I found @Steven's answer is too slow, so I came up with the following:

 public class AssemblyItem { public Assembly Item { get; set; } public IList<AssemblyItem> Dependencies { get; set; } public AssemblyItem(Assembly item) { Item = item; Dependencies = new List<AssemblyItem>(); } } public static void Main() { // Get the assemblies var assemblyItems = BuildManager.GetReferencedAssemblies().Cast<Assembly>().OrderBy(a => a.FullName).Select(a => new AssemblyItem(a)).ToList(); // Add the dependencies foreach (var item in assemblyItems) { foreach (var reference in item.Item.GetReferencedAssemblies()) { var dependency = assemblyItems.SingleOrDefault(i => i.Item.FullName == reference.FullName); if (dependency != null) item.Dependencies.Add(dependency); } } // Sort the assemblies var sortedAssemblyItems = assemblyItems.TSort(i => i.Dependencies); } 

It uses the TSort extension method from:

fooobar.com/questions/92440 / ...

For more on how this works, see the following Wikipedia article:

http://en.wikipedia.org/wiki/Topological_sort

0
source

I solved it as:

 public class AssemblyInfo { public readonly Assembly Item; public readonly IList<AssemblyInfo> ReferencedAssemblies; public AssemblyInfo(Assembly item) { Item = item ?? throw new NullReferenceException("Item is null"); ReferencedAssemblies = new List<AssemblyInfo>(); } int Count() { return ReferencedAssemblies.Count; } public override string ToString() { return Item.FullName; } public IEnumerable<AssemblyInfo> OrderedDependencies() { List<AssemblyInfo> localOrdered = new List<AssemblyInfo>(); foreach (AssemblyInfo item in ReferencedAssemblies.OrderBy(t => t.Count())) { IEnumerable<AssemblyInfo> temp = item.OrderedDependencies(); localOrdered = localOrdered.Union<AssemblyInfo>(temp).ToList(); } localOrdered.Add(this); return localOrdered; } public override bool Equals(object obj) { //Check whether any of the compared objects is null. if (Object.ReferenceEquals(obj, null)) { return false; } //Check whether the compared objects reference the same data. if (Object.ReferenceEquals(this, obj)) { return true; } return Item.FullName.Equals(((AssemblyInfo)obj).Item.FullName); } public override int GetHashCode() { //Get hash code for the Name field if it is not null. return Item.FullName.GetHashCode(); } public static AssemblyInfo Parse(string assembliesPath, string assemblyName) { return Parse(assembliesPath, assemblyName, new Dictionary<string, Assembly>()); } static AssemblyInfo Parse(string assembliesPath, string assemblyName, Dictionary<string, Assembly> loadedAssemblies) { string assemblyFullPath = Path.Combine(assembliesPath, assemblyName); if (!File.Exists(assemblyFullPath)) { return null; } if (loadedAssemblies == null) { loadedAssemblies = new Dictionary<string, Assembly>(); } if (!loadedAssemblies.ContainsKey(assemblyFullPath)) { loadedAssemblies.Add(assemblyFullPath, Assembly.Load(File.ReadAllBytes(assemblyFullPath))); } Assembly a = loadedAssemblies[assemblyFullPath]; AssemblyInfo ai = new AssemblyInfo(a); foreach (AssemblyName an in a.GetReferencedAssemblies()) { AssemblyInfo d = Parse(assembliesPath, an.Name + ".dll", loadedAssemblies); if (d != null) { ai.ReferencedAssemblies.Add(d); } } return ai; } } 

Use it:

 AssemblyInfo ai = AssemblyInfo.Parse("assembliesPath","yourassembly.dll"); IEnumerable<AssemblyInfo> sorted = ai.OrderedDependencies(); foreach (AssemblyInfo item in sorted) { Console.WriteLine(item.Item.ManifestModule.ToString()); } 
0
source

Source: https://habr.com/ru/post/1499122/


All Articles