Assembly Markers

Assembly markers are just “dummy” types you create to easily refer to an assembly in a safe, refactor-friendly way.

They’re super handy in multi-project / Clean Architecture .NET solutions when you want to:

  • Scan assemblies (Scrutor, MediatR, AutoMapper, FluentValidation, etc.)

  • Load embedded resources

  • Keep dependencies directional and explicit

Let’s walk through what they are and how you actually use them in a .NET 10 / C# 10 solution.

1. What is an “assembly marker”?

An assembly marker is usually:

  • An empty class, or

  • An empty interface, or

  • A static class

whose only job is to give you a strongly-typed way to get its assembly:

var assembly = typeof(MyAssemblyMarker).Assembly;

Instead of doing things like:

var assembly = typeof(SomeRandomKnownTypeInThisAssembly).Assembly;

…which is brittle, unclear, and annoying to change later.

2. Typical project layout with markers

Say you have a Clean Architecture style solution:

  • MyApp.Domain

  • MyApp.Application

  • MyApp.Infrastructure

  • MyApp.Web (API / UI)

You can add one marker per project.

MyApp.Domain – DomainAssemblyMarker.cs

namespace MyApp.Domain;

// File-scoped namespace; marker is empty
public sealed class DomainAssemblyMarker
{
}

MyApp.Application – ApplicationAssemblyMarker.cs

namespace MyApp.Application;

public sealed class ApplicationAssemblyMarker
{
}

MyApp.Infrastructure – InfrastructureAssemblyMarker.cs

namespace MyApp.Infrastructure;

public sealed class InfrastructureAssemblyMarker
{
}

MyApp.Web – WebAssemblyMarker.cs

namespace MyApp.Web;

public sealed class WebAssemblyMarker
{
}

Now any other project can get that assembly via typeof(WhateverAssemblyMarker).Assembly without needing to know about “some concrete type that happens to live there”.

3. Usage Example #1 – Registering MediatR by assembly

Goal: Register all MediatR handlers from the Application layer.

In MyApp.Web / Program.cs:

using MyApp.Application;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssembly(typeof(ApplicationAssemblyMarker).Assembly);
});

var app = builder.Build();
// ...
app.Run();

Here:

  • typeof(ApplicationAssemblyMarker).Assembly gives you the Application project’s assembly.

  • You don’t need to pick a handler class as the “anchor type”.

4. Usage Example #2 – AutoMapper configuration from a layer

Goal: Scan all AutoMapper profiles in Application.

using MyApp.Application;
// ...

builder.Services.AddAutoMapper(typeof(ApplicationAssemblyMarker).Assembly);

If later you move Profiles between folders/namespaces, this keeps working as long as the assembly marker stays in that project.

5. Usage Example #3 – Scrutor scanning for DI

Assembly markers shine with Scrutor (or any reflection-based DI scanning).

Suppose we want:

  • Classes ending with Service in Application

  • Registered as ImplementedInterfaces

  • Scoped lifetime

In Program.cs of MyApp.Web:

using MyApp.Application;
using MyApp.Infrastructure;
using Scrutor;

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;

services.Scan(scan => scan
    .FromAssemblies(
        typeof(ApplicationAssemblyMarker).Assembly,
        typeof(InfrastructureAssemblyMarker).Assembly)
    .AddClasses(classes => classes
        .InNamespaces("MyApp.Application.Services")
        .Where(type => type.Name.EndsWith("Service")))
    .AsImplementedInterfaces()
    .WithScopedLifetime());

Key bits:

  • FromAssemblies(...) uses the markers, not a random “known type”.

  • If you rename a class or move it to another folder, the scan still works.

6. Usage Example #4 – FluentValidation registration

using FluentValidation;
using MyApp.Application;

builder.Services.AddValidatorsFromAssembly(
    typeof(ApplicationAssemblyMarker).Assembly);

Again, we’re telling FluentValidation: “scan this assembly”, anchored on the marker.

7. Usage Example #5 – Embedded resources

Assembly markers are also useful whenever you need Assembly to load resources.

using System.Reflection;
using MyApp.Infrastructure;

public sealed class SqlScriptLoader
{
    public string LoadInitScript()
    {
        var assembly = typeof(InfrastructureAssemblyMarker).Assembly;

        using var stream = assembly.GetManifestResourceStream(
            "MyApp.Infrastructure.Scripts.InitDatabase.sql");

        if (stream is null)
            throw new InvalidOperationException("Embedded SQL script not found.");

        using var reader = new StreamReader(stream);
        return reader.ReadToEnd();
    }
}

You never have to guess which assembly the resource lives in; you’ve anchored it to the marker.

8. Why not just use Assembly.GetExecutingAssembly()?

Because:

  • In ASP.NET / worker services, the executing assembly may not be the one you want.

  • If code moves into a shared library, executing/entry assembly changes.

  • Markers let you refer directly to the correct assembly with compile-time safety.

Assembly marker pattern:

  • Is explicit

  • Survives refactors (namespaces/classnames can safely change elsewhere)

  • Matches Clean Architecture layering (each project “exports” a single known anchor type)

9. Marker as interface vs class vs static class

You’ll see variations:

Empty class (most common)

public sealed class ApplicationAssemblyMarker
{
}

Pros: trivial, can’t be implemented anywhere else.

Interface

public interface IApplicationAssemblyMarker
{
}

Then you have a single class implementing it, and reference that type. Slightly more ceremony; sometimes used when people also use the interface as a “semantic marker” for types within the assembly.

Static class

public static class ApplicationAssemblyMarker
{
}

Also fine; you just need a Type, so any type works.

TL;DR: use a sealed empty class per assembly; it’s simple and clear.

10. Putting it together – Clean Architecture + markers

Quick mental model for your .NET 10 solution:

  • Domain: core entities, value objects, domain events
    DomainAssemblyMarker

  • Application: use cases, commands, queries, validators, mapping profiles
    ApplicationAssemblyMarker

  • Infrastructure: EF Core, external APIs, implementations
    InfrastructureAssemblyMarker

  • Web: controllers, endpoints, DI composition root
    WebAssemblyMarker (optional, but nice for consistency)

Then in Web (or whatever is your composition root):

builder.Services
    .AddMediatR(cfg =>
        cfg.RegisterServicesFromAssembly(typeof(ApplicationAssemblyMarker).Assembly))
    .AddAutoMapper(typeof(ApplicationAssemblyMarker).Assembly)
    .AddValidatorsFromAssembly(typeof(ApplicationAssemblyMarker).Assembly);

builder.Services.Scan(scan => scan
    .FromAssemblies(
        typeof(ApplicationAssemblyMarker).Assembly,
        typeof(InfrastructureAssemblyMarker).Assembly)
    .AddClasses()
    .AsImplementedInterfaces()
    .WithScopedLifetime());

That’s the practical, idiomatic way to use assembly markers in a modern C# 10 / .NET 10 project.

#dotnet #csharp #dotnet10

Next
Next

.Net 10 project files explained