Skip to content

Commit 5e164c4

Browse files
committed
Types.InAssembly - add option to load referenced assemblies
1 parent f0c1cac commit 5e164c4

File tree

6 files changed

+84
-13
lines changed

6 files changed

+84
-13
lines changed

samples/NetArchTest.SampleTests/NetArchTest.SampleTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
<ItemGroup>
2020
<ProjectReference Include="..\..\sources\NetArchTest\NetArchTest.csproj" />
21+
<ProjectReference Include="..\SampleApp.API\SampleApp.API.csproj" />
2122
<ProjectReference Include="..\SampleApp.ModuleAlpha\SampleApp.ModuleAlpha.csproj" />
2223
<ProjectReference Include="..\SampleApp.ModuleOmega\SampleApp.ModuleOmega.csproj" />
2324
</ItemGroup>

samples/SampleApp.API/SampleApp.API.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77
<InvariantGlobalization>true</InvariantGlobalization>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<ProjectReference Include="..\SampleApp.ModuleAlpha\SampleApp.ModuleAlpha.csproj" />
12+
<ProjectReference Include="..\SampleApp.ModuleOmega\SampleApp.ModuleOmega.csproj" />
13+
</ItemGroup>
14+
1015
</Project>

sources/NetArchTest/Assemblies/AssemblySpec.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ internal sealed class AssemblySpec
1010
{
1111
private AssemblyDefinition assemblyDefinition;
1212
private IReadOnlyList<TypeDefinition> typeDefinitions;
13+
private List<AssemblySpec> referenced = new List<AssemblySpec>();
14+
15+
public string FullName => assemblyDefinition.FullName;
16+
1317

1418

1519
public AssemblySpec(AssemblyDefinition assemblyDefinition, IEnumerable<TypeDefinition> typeDefinitions)
@@ -18,13 +22,18 @@ public AssemblySpec(AssemblyDefinition assemblyDefinition, IEnumerable<TypeDefin
1822
this.typeDefinitions = typeDefinitions.ToArray();
1923
}
2024

25+
public void AddRef(AssemblySpec assemblySpec)
26+
{
27+
referenced.Add(assemblySpec);
28+
}
2129

2230
public IEnumerable<TypeSpec> GetTypes()
2331
{
2432
return typeDefinitions.Select(x => new TypeSpec(x));
2533
}
2634

2735

36+
2837
public IAssembly CreateWrapper()
2938
{
3039
return new AssemblyContainer(assemblyDefinition);

sources/NetArchTest/Assemblies/DataLoader.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal static class DataLoader
1717
"netstandard",
1818
"NuGet",
1919
"Newtonsoft",
20-
"Xunit",
20+
"xunit",
2121
"Internal.Microsoft",
2222
"Mono.Cecil",
2323
"NetArchTest.Assemblies",
@@ -38,36 +38,65 @@ public static LoadedData LoadFromCurrentDomain()
3838

3939
return LoadFromAssemblies(selectedAssemblies);
4040
}
41-
public static LoadedData LoadFromAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null)
41+
public static LoadedData LoadFromAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)
4242
{
4343
var files = assemblies.Select(x => x.Location);
4444

45-
return LoadFromFiles(files, searchDirectories);
45+
return LoadFromFiles(files, searchDirectories, loadReferencedAssemblies);
4646
}
47-
public static LoadedData LoadFromFiles(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories = null)
47+
public static LoadedData LoadFromFiles(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)
4848
{
49-
var assemblies = Load(fileNames, searchDirectories);
49+
var assemblies = Load(fileNames, searchDirectories, loadReferencedAssemblies);
5050

5151
return new LoadedData(assemblies);
5252
}
5353

5454

55-
private static IEnumerable<AssemblySpec> Load(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories = null)
55+
private static IEnumerable<AssemblySpec> Load(IEnumerable<string> fileNames, IEnumerable<string> searchDirectories, bool loadReferencedAssemblies)
5656
{
5757
var readerParameters = CreateReaderParameters(searchDirectories);
58+
var definitions = new Dictionary<string, AssemblySpec>();
5859

5960
foreach (var fileName in fileNames)
6061
{
6162
var assemblyDefinition = ReadAssemblyDefinition(fileName, readerParameters);
62-
if (assemblyDefinition == null) continue;
63+
ProcessAssemblyDefinition(null, assemblyDefinition);
64+
}
6365

64-
if (exclusionTree.GetAllMatchingNames(assemblyDefinition.Name.Name).Any() == true) continue;
66+
void ProcessAssemblyDefinition(AssemblySpec parent, AssemblyDefinition assemblyDefinition)
67+
{
68+
if (assemblyDefinition == null) return;
69+
if (exclusionTree.GetAllMatchingNames(assemblyDefinition.Name.Name).Any() == true) return;
70+
if (definitions.TryGetValue(assemblyDefinition.FullName, out var existingSpec))
71+
{
72+
parent?.AddRef(existingSpec);
73+
return;
74+
}
6575

6676
var typeDefinitions = ReadTypes(assemblyDefinition);
77+
var spec = new AssemblySpec(assemblyDefinition, typeDefinitions);
78+
79+
definitions.Add(assemblyDefinition.FullName, spec);
80+
parent?.AddRef(spec);
6781

68-
yield return new AssemblySpec(assemblyDefinition, typeDefinitions);
82+
if (loadReferencedAssemblies == false) return;
83+
84+
foreach (var module in assemblyDefinition.Modules)
85+
{
86+
if (module.HasAssemblyReferences)
87+
{
88+
foreach (var reference in module.AssemblyReferences)
89+
{
90+
var refAssembly = module.AssemblyResolver.Resolve(reference);
91+
ProcessAssemblyDefinition(spec, refAssembly);
92+
}
93+
}
94+
}
6995
}
96+
97+
return definitions.Values;
7098
}
99+
71100
private static ReaderParameters CreateReaderParameters(IEnumerable<string> searchDirectories, bool readSymbols = true)
72101
{
73102
DefaultAssemblyResolver assemblyResolver = null;

sources/NetArchTest/Types.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ public static Types InCurrentDomain()
4141
/// <param name="assembly">The assembly to base the list on.</param>
4242
/// <param name="searchDirectories">An optional list of search directories to allow resolution of referenced assemblies.</param>
4343
/// <returns>A list of types that can have predicates and conditions applied to it.</returns>
44-
public static Types InAssembly(Assembly assembly, IEnumerable<string> searchDirectories = null)
44+
public static Types InAssembly(Assembly assembly, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)
4545
{
46-
return Types.InAssemblies(new List<Assembly> { assembly }, searchDirectories);
46+
return Types.InAssemblies(new List<Assembly> { assembly }, searchDirectories, loadReferencedAssemblies);
4747
}
4848

4949
/// <summary>
@@ -52,9 +52,9 @@ public static Types InAssembly(Assembly assembly, IEnumerable<string> searchDire
5252
/// <param name="assemblies">The assemblies to base the list on.</param>
5353
/// <param name="searchDirectories">An optional list of search directories to allow resolution of referenced assemblies.</param>
5454
/// <returns>A list of types that can have predicates and conditions applied to it.</returns>
55-
public static Types InAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null)
55+
public static Types InAssemblies(IEnumerable<Assembly> assemblies, IEnumerable<string> searchDirectories = null, bool loadReferencedAssemblies = false)
5656
{
57-
return new Types(DataLoader.LoadFromAssemblies(assemblies, searchDirectories));
57+
return new Types(DataLoader.LoadFromAssemblies(assemblies, searchDirectories, loadReferencedAssemblies));
5858
}
5959

6060
/// <summary>

tests/NetArchTest.Rules.UnitTests/TypesTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.IO;
22
using System.Linq;
33
using System.Reflection;
4+
using NetArchTest.CrossAssemblyTest.A;
45
using NetArchTest.Rules;
56
using Xunit;
67

@@ -98,5 +99,31 @@ public void FromFile_BadImage_CaughtAndEmptyListReturned()
9899
// Assert
99100
Assert.Empty(result);
100101
}
102+
103+
104+
[Fact(DisplayName = "InAssembly can load types from referenced assemblies")]
105+
public void InAssembly_LoadReferencedAssemblies_True()
106+
{
107+
// Act
108+
var result = Types.InAssembly(typeof(TypesTests).Assembly, loadReferencedAssemblies: true)
109+
.GetTypes()
110+
.Select(x => x.ReflectionType)
111+
.ToArray();
112+
113+
// Assert
114+
Assert.Contains(typeof(BaseClassFromA), result);
115+
}
116+
[Fact(DisplayName = "InAssembly can not load types from referenced assemblies")]
117+
public void InAssembly_LoadReferencedAssemblies_False()
118+
{
119+
// Act
120+
var result = Types.InAssembly(typeof(TypesTests).Assembly, loadReferencedAssemblies: false)
121+
.GetTypes()
122+
.Select(x => x.ReflectionType)
123+
.ToArray();
124+
125+
// Assert
126+
Assert.DoesNotContain(typeof(BaseClassFromA), result);
127+
}
101128
}
102129
}

0 commit comments

Comments
 (0)