The model

edit

The assertions are built on a model of the assemblies that you can query directly, to write checks the assertions do not cover:

var architecture = ArchitectureModel.FromAssemblyContaining<Order>();
var controller = architecture.GetTypeElement(typeof(OrdersController));

var usedTypes = controller.Dependencies
    .Select(dependency => dependency.Target.FullName)
    .Where(name => name.StartsWith("SampleApp.", StringComparison.Ordinal))
    .Distinct()
    .Order(StringComparer.Ordinal)
    .ToList();

string[] expected = ["SampleApp.Application.PlaceOrderHandler", "SampleApp.Domain.IOrderRepository", "SampleApp.Domain.OrderLine", "SampleApp.Infrastructure.SqlOrderRepository"];
Assert.Equal(expected, usedTypes);

Types and members

FullName is the name as written in metadata, which is also the value of Type.FullName: MyApp.Repository`1, MyApp.Outer+Inner. Types are compared by full name, without their assembly: the types of .NET are often referenced from a facade assembly such as System.Runtime and defined in another one.

Dependencies

Dependencies lists the types a type uses. Each Dependency has the member that uses the type, the DependencyKind, the method or field used, and the source location. A type depends on the types it uses in:

  • its declaration: base type, interfaces, attributes and their typeof arguments, generic constraints;
  • the declarations of its members: the types of the fields, properties and events, the parameters and return types;
  • the code of its methods: the methods it calls, the objects it creates, the fields it accesses, the types of its local variables, the types it casts to, tests with is, or uses with typeof, and the exceptions it catches.

A generic type gives a dependency on each of its parts: List<Order> gives a dependency on List<T> and one on Order.

The code the compiler generates belongs to the member that was written: the code of a lambda, a local function, an iterator or an async method is a dependency of the method that declares it, and the variables they capture are LocalVariable dependencies. The types the compiler generates (closures, state machines, anonymous types) are not part of Types.

A type does not depend on itself, on System.Void, or on the attributes the compiler adds on its own, such as NullableAttribute or CompilerGeneratedAttribute. The dependencies reflect the compiled code, so they include the types the compiler uses to implement a feature, such as AsyncTaskMethodBuilder for an async method.