Hidden Magic: Things That Just Work Without You Knowing

cover

The ABP Framework is famous for its Convention-over-Configuration approach, which means a lot of things work automatically without explicit configuration. In this article, I'll uncover these "hidden magics" that make ABP so powerful but often go unnoticed by developers.


1. Automatic Service Registration Without Any Attributes

The Magic: Any class implementing ITransientDependency, ISingletonDependency, or IScopedDependency is automatically registered with the corresponding lifetime.

// This is automatically registered as Transient - no configuration needed!
public class MyService : IMyService, ITransientDependency
{
    public void DoSomething() { }
}

Where it happens: Volo.Abp.Core/Volo/Abp/DependencyInjection/ConventionalRegistrarBase.cs

The framework scans all assemblies and automatically determines service lifetime from class hierarchy. This is why you rarely need to manually register services in ABP.


2. All Interfaces Are Exposed By Default

The Magic: When you register a service, it's automatically registered as both itself AND all its implemented interfaces.

public class UserService : IUserService, IValidationInterceptor
{
    // Registered as both IUserService AND IValidationInterceptor
    // No ExposeServices attribute needed!
}

Where it happens: Volo.Abp.Core/DependencyInjection/ExposedServiceExplorer.cs:9-14

private static readonly ExposeServicesAttribute DefaultExposeServicesAttribute =
    new ExposeServicesAttribute
    {
        IncludeDefaults = true,
        IncludeSelf = true
    };

3. Automatic Validation on Every Method

The Magic: Every application service method parameters are automatically validated - you don't need to add [Validate] attributes.

Where it happens: Volo.Abp.Validation/ValidationInterceptorRegistrar.cs

The ValidationInterceptor is automatically added to the interceptor pipeline for all services. Every method call triggers automatic validation of input parameters.


4. Automatic Unit of Work Management

The Magic: Every database operation is automatically wrapped in a transaction. You don't need to explicitly configure unit of work for most scenarios.

Where it happens: The UnitOfWorkInterceptor is auto-registered and automatically:

  • Begins transaction before method execution
  • Commits on success
  • Rolls back on exception

5. Auditing Is Enabled By Default

The Magic: Auditing is ON by default, even for anonymous users!

public class AbpAuditingOptions
{
    public AbpAuditingOptions()
    {
        IsEnabled = true;                    // Enabled by default!
        IsEnabledForAnonymousUsers = true;   // Anonymous users are audited!
        HideErrors = true;                   // Errors are silently hidden
        AlwaysLogOnException = true;         // Exceptions always logged
    }
}

Where it happens: Volo.Abp.Auditing/AbpAuditingOptions.cs:73-91

This means every entity change and service call is logged automatically unless explicitly disabled.


6. Security Logging Is Always On

The Magic: Security logging is enabled by default in ABP!

public AbpSecurityLogOptions()
{
    IsEnabled = true;  // Hidden: ON by default!
}

Every authentication attempt, authorization failure, and security-relevant action is logged automatically.


7. Data Filters Are Enabled By Default

The Magic: ISoftDelete and IMultiTenant filters are enabled by default.

// In DataFilter.cs - Line 103
_filter.Value = _options.DefaultStates.GetOrDefault(typeof(TFilter))?.Clone() 
    ?? new DataFilterState(true);  // true = enabled!

This means:

  • Deleted entities are automatically filtered out
  • Multi-tenant data is automatically isolated

You must explicitly disable these filters when you need to access all data:

using (_dataFilter.Disable<IMultiTenant>())
{
    // Query all tenants
}

8. Object Mapping (Mapperly - The New Standard)

The Magic: Starting with ABP v9.0, new project templates use Mapperly instead of AutoMapper. Any class using Mapperly attributes is automatically configured.

// Starting with ABP v10.0, new projects use Mapperly instead of AutoMapper

// Inherit from MapperBase - automatically registered with IObjectMapper
public partial class UserMapper : MapperBase<User, UserDto>
{
    public override partial UserDto Map(User source);
}

// For two-way mapping
public partial class UserTwoWayMapper : TwoWayMapperBase<User, UserDto>
{
    public override partial UserDto Map(User source);
    public override partial User ReverseMap(UserDto source);
}

The mapping is done at compile-time (no reflection overhead), and it's automatically registered with ABP's IObjectMapper.

Where it happens: Volo.Abp.Mapperly/AbpMapperlyConventionalRegistrar.cs

// Automatically discovers and configures all Mapperly mappers
context.Services.OnRegistered(context =>
{
    if (typeof(MapperBase).IsAssignableFrom(context.ImplementationType))
    {
        // Register the mapper
    }
});

9. Automatic Data Seed Contributor Discovery

The Magic: Any class implementing IDataSeedContributor is automatically discovered and executed on application startup.

// Automatically discovered and run on startup!
public class MyDataSeeder : IDataSeedContributor
{
    public Task SeedAsync(DataSeedContext context)
    {
        // Seed data here
    }
}

Where it happens: Volo.Abp.Data/AbpDataModule.cs:40-56


10. Automatic Definition Provider Discovery

The Magic: These are all auto-discovered without any configuration:

  • ISettingDefinitionProvider - Settings
  • IPermissionDefinitionProvider - Permissions
  • IFeatureDefinitionProvider - Features
  • INavigationProvider - Navigation items

11. Automatic Widget Discovery

The Magic: Any class implementing IWidget is automatically registered and can be rendered in pages.

Where it happens: Volo.Abp.AspNetCore.Mvc.UI.Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs


12. Remote Services Are Enabled By Default

The Magic: All API controllers have remote service functionality enabled by default:

public class RemoteServiceAttribute : Attribute
{
    public bool IsEnabled { get; set; } = true;  // Enabled by default!
}

13. Auto API Controllers - Application Services Become REST APIs Automatically

The Magic: When you create an application service (class implementing an interface or inheriting from ApplicationService), ABP automatically creates REST API endpoints for it - no manual controller needed!

// This interface is automatically exposed as /api/app/product
public interface IProductAppService
{
    Task<List<ProductDto>> GetListAsync();
    Task<ProductDto> CreateAsync(CreateProductDto input);
    Task DeleteAsync(Guid id);
}

// The implementation automatically becomes an API Controller
public class ProductAppService : ApplicationService, IProductAppService
{
    public Task<List<ProductDto>> GetListAsync() { ... }
    public Task<ProductDto> CreateAsync(CreateProductDto input) { ... }
    public Task DeleteAsync(Guid id) { ... }
}

// Available endpoints (auto-generated):
// GET  /api/app/product
// POST /api/app/product
// DELETE /api/app/product/{id}

Where it happens: Volo.Abp.AspNetCore.Mvc/AbpServiceConvention.cs

The framework:

  • Converts camelCase method names to kebab-case routes
  • Maps HTTP methods automatically (Get→GET, Create→POST, Delete→DELETE)
  • Generates proper DTOs from parameters and return types
  • Handles serialization/deserialization

14. Dynamic Client Proxies - Client-Side Code Generated Automatically

The Magic: On the client side, you don't need to write HTTP client code. ABP automatically generates Dynamic JavaScript Proxies and Dynamic C# Proxies that let you call your APIs as if they were local method calls!

JavaScript (MVC/Razor Pages):

// Just call it like a local function!
var products = await productAppService.getList();
await productAppService.create({ name: "New Product" });
await productAppService.delete(id);

C# (Blazor/Console Apps):

// Inject and use like local method calls!
public class ProductListModel : PageModel
{
    private readonly IProductAppService _productAppService;
    
    public async Task OnGetAsync()
    {
        // Actually makes HTTP call to the server!
        var products = await _productAppService.GetListAsync();
    }
}

Where it happens:

  • JavaScript: Volo.Abp.AspNetCore.Mvc.UI - Dynamic JavaScript proxies
  • C#: Volo.Abp.AspNetCore.Mvc.Client - Dynamic C# HTTP clients

This is why you can inject application service interfaces directly in Blazor and call them like local methods!


15. Permission Checks

By default, all application service methods and controllers are public and accessible. Add [Authorize] or [AbpAuthorize] to restrict access:

[Authorize]
public async Task CreateAsync(CreateDto input) { }

[AbpAuthorize("MyApp.Permissions.CanCreate")]
public async Task CreateAsync(CreateDto input) { }

The AuthorizationInterceptor is added only when [Authorize] attribute is present on the class or method.


16. Background Workers Auto-Registration

The Magic: Background workers are enabled by default, and any class implementing IBackgroundWorker or IQuartzBackgroundWorker is auto-registered.


17. Entity ID Generation

The Magic: ABP automatically detects the best ID generation strategy based on the entity type:

  • Guid → Auto-generates GUID
  • int/long → Database identity
  • string → No auto-generation (must provide)

Where it happens: Volo.Abp.Ddd.Domain/Entities/EntityHelper.cs


18. Anti-Forgery Token Magic

The Magic: ABP automatically handles CSRF protection with these hardcoded values:

// Blazor Client
private const string AntiForgeryCookieName = "XSRF-TOKEN";
private const string AntiForgeryHeaderName = "RequestVerificationToken";

19. Automatic Event Handler Discovery

The Magic: Any class implementing IEventHandler<TEvent> is automatically subscribed to handle events - no manual registration needed!

// This handler is automatically registered when the assembly loads!
public class OrderCreatedHandler : IEventHandler<OrderCreatedEvent>
{
    public Task HandleEventAsync(OrderCreatedEvent eventData)
    {
        // Handle the event - automatically subscribed!
    }
}

20. Unit of Work Events - Automatic Save

The Magic: Events are not fired immediately - they're collected during the unit of work and fired at the end when everything succeeds!

// In UnitOfWorkEventPublisher.cs
// Events are queued and published only when UOW successfully completes
await _localEventBus.PublishAsync(
    entityChangeEvent,
    onUnitOfWorkComplete: true  // Wait for UOW to complete!
);

This ensures transactional consistency - if your UOW fails, no events are fired.


21. Distributed Event Bus - Outbox Pattern

The Magic: ABP implements the Outbox Pattern automatically for distributed events, ensuring no events are lost!

// In DistributedEventBusBase.cs
// Events are stored in outbox table and processed reliably
foreach (var outboxConfig in AbpDistributedEventBusOptions.Outboxes.Values.OrderBy(x => x.Selector is null))
{
    // Outbox processing happens automatically
}

22. Automatic Object Extension Properties

The Magic: Any property decorated with [DisableAuditing] is automatically excluded from audit logs without any configuration!

// This property is automatically excluded from auditing
[DisableAuditing]
public string SecretData { get; set; }

23. Virtual File System

The Magic: ABP provides a virtual file system that merges embedded resources from all modules into a single virtual path!

// Any file embedded as "EmbeddedResource" is accessible virtually
// No configuration needed for module authors!

Where it happens: Volo.Abp.VirtualFileSystem/AbpVirtualFileSystemModule.cs

This is how ABP modules include static files (CSS, JS, images) that work without copying to wwwroot.


24. Automatic JSON Serialization Settings

The Magic: ABP pre-configures JSON serialization with:

  • Camel case property naming
  • Null value handling
  • Reference loop handling
  • Custom converters for common types

All configured automatically.


25. Token-Based Configuration Magic

The Magic: Many settings support tokens that are resolved at runtime:

// These tokens are automatically replaced
"ConnectionStrings": {
    "Default": "Server=localhost;Database=MyApp_{TENANT_ID}"
}
  • {TENANT_ID} - Replaced with current tenant ID
  • {USER_ID} - Replaced with current user ID
  • {CURRENT_TIME} - Replaced with current datetime

26. Localization Automatic Discovery

The Magic: All .json localization files in the application are automatically discovered and loaded:

/Localization/MyApp/
  en.json
  tr.json
  de.json

No explicit registration needed - just add files and they're available!


27. Feature Checks

Add [RequiresFeature] to restrict access based on feature flags:

[RequiresFeature("MyApp.Features.SomeFeature")]
public async Task DoSomethingAsync()
{
}

The FeatureInterceptor is added only when [RequiresFeature] attribute is present on the class or method.


28. API Versioning Convention

The Magic: ABP automatically handles API versioning with sensible defaults:

  • Default version: 1.0
  • Version from URL path: /api/v1/...
  • Version from header: Accept: application/json;v=1.0

All configured automatically unless overridden.


29. Health Check Endpoints

The Magic: Health check endpoints are auto-registered:

  • /health - Overall health status
  • /health/ready - Readiness check
  • /health/live - Liveness check

Includes automatic checks for:

  • Database connectivity
  • Cache availability
  • External services

30. Swagger/OpenAPI Auto-Configuration

The Magic: If you reference Volo.Abp.AspNetCore.Mvc.UI.Swagger, Swagger UI is automatically generated with:

  • All API endpoints documented
  • Authorization support
  • Versioning support
  • XML documentation

No configuration needed beyond the package reference!


31. Tenant Resolution Priority Chain

The Magic: ABP automatically resolves tenants in this order (first match wins):

  1. Route - From URL path (/api/v1/[tenant]/...)
  2. Query String - From __tenant query parameter
  3. Header - From __tenant header
  4. Cookie - From __tenant cookie
  5. Subdomain - From URL subdomain (tenant.domain.com)

32. Background Job Queue Magic

The Magic: Background jobs are automatically retried with exponential backoff:

// Jobs are automatically:
// - Queued when published
// - Retried on failure (3 times default)
// - Delayed with exponential backoff

Where it happens: Volo.Abp.BackgroundJobs/AbpBackgroundJobOptions.cs


Summary Table

# Feature Default Behavior You Need to Know
1 Service Registration Auto by interface Implement ITransientDependency
2 Service Exposure Self + all interfaces Default is generous
3 Validation All methods validated Happens automatically
4 Unit of Work Transactional by default Auto-commits/rollbacks
5 Auditing Enabled + anonymous users Can disable per entity/method
6 Security Log Always on Can configure what to log
7 Soft Delete Filter Enabled by default Must disable to query deleted
8 Multi-Tenancy Filter Enabled by default Must disable for host data
9 Object Mapping Mapperly (compile-time) Inherit from MapperBase
10 Data Seeds Auto-discovery Implement IDataSeedContributor
11 Remote Services Enabled by default Can disable per service/method
12 Auto API Controllers App services → REST APIs No manual controller needed
13 Dynamic Client Proxies Auto-generated Call APIs like local methods
14 Permissions NOT automatic Must add [Authorize]
15 Settings Auto-discovery Define via ISettingDefinitionProvider
16 Features NOT automatic Must add [RequiresFeature]
17 Background Workers Auto-registration Implement IBackgroundWorker
18 Entity ID Generation Auto by type Guid, int, string strategies
19 Anti-Forgery Auto-enabled Token cookie/header handling
20 Event Handlers Auto-discovery Implement IEventHandler<TEvent>
21 UOW Events Deferred execution Transactional consistency
22 Distributed Events Outbox pattern Reliable messaging
23 Virtual Files Module merging Embedded resources as virtual
24 JSON Settings Pre-configured CamelCase, null handling
25 Token Replacement Runtime resolution {TENANT_ID}, {USER_ID}
26 Localization Auto-discovery JSON files in /Localization
27 API Versioning Default v1.0 URL, header, query support
28 Health Checks Auto-registered /health, /health/ready, /health/live
29 Swagger Auto-generated With authorization support
30 Tenant Resolution Multi-source chain Route → Query → Header → Cookie
31 Job Retries Auto with backoff 3 retries default
32 Widgets Auto-discovery Implement IWidget

Conclusion

ABP Framework's hidden magic is what makes it so productive to use. These conventions allow developers to focus on business logic rather than boilerplate configuration. However, understanding these defaults is crucial for:

  1. Debugging - Knowing why certain behaviors happen
  2. Optimization - Disabling what you don't need
  3. Security - Understanding what's logged/audited by default
  4. Architecture - Following the intended patterns

The next time something "just works" in ABP, there's likely a hidden convention behind it!


What hidden ABP magic have you discovered? Share your findings in the comments!