| name | abp-core |
| description | Core ABP Framework conventions - module system, DI registration, base classes (ApplicationService, DomainService), IClock, BusinessException, localization, async patterns. Use when working on any ABP project, asking about ABP fundamentals, or unsure which skill applies. |
ABP Core Conventions
Documentation: https://abp.io/docs/latest
API Reference: https://abp.io/docs/api/
Key Rules
- Use
IClock / Clock.Now instead of DateTime.Now / DateTime.UtcNow
- Use
ITransientDependency / ISingletonDependency instead of AddScoped/AddTransient/AddSingleton
- Use
IRepository<T> instead of injecting DbContext directly
- Check base class properties (
Clock, CurrentUser, GuidGenerator, L) before injecting services
- Use
BusinessException with namespaced error codes for domain rule violations
Module System
Every ABP application/module has a module class that configures services:
[DependsOn(
typeof(AbpDddDomainModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class MyAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}
Note: Middleware configuration (OnApplicationInitialization) should only be done in the final host application, not in reusable modules.
Dependency Injection Conventions
Automatic Registration
ABP automatically registers services implementing marker interfaces:
ITransientDependency → Transient lifetime
ISingletonDependency → Singleton lifetime
IScopedDependency → Scoped lifetime
Classes inheriting from ApplicationService, DomainService, AbpController are also auto-registered.
Repository Usage
You can use the generic IRepository<TEntity, TKey> for simple CRUD operations. Define custom repository interfaces only when you need custom query methods:
public class BookAppService : ApplicationService
{
private readonly IRepository<Book, Guid> _bookRepository;
}
public interface IBookRepository : IRepository<Book, Guid>
{
Task<Book> FindByNameAsync(string name);
}
public class BookAppService : ApplicationService
{
private readonly IBookRepository _bookRepository;
}
Exposing Services
[ExposeServices(typeof(IMyService))]
public class MyService : IMyService, ITransientDependency { }
Important Base Classes
| Base Class | Purpose |
|---|
Entity<TKey> | Basic entity with ID |
AggregateRoot<TKey> | DDD aggregate root |
DomainService | Domain business logic |
ApplicationService | Use case orchestration |
AbpController | REST API controller |
ABP base classes already inject commonly used services as properties. Before injecting a service, check if it's already available:
| Property | Available In | Description |
|---|
GuidGenerator | All base classes | Generate GUIDs |
Clock | All base classes | Current time (use instead of DateTime) |
CurrentUser | All base classes | Authenticated user info |
CurrentTenant | All base classes | Multi-tenancy context |
L (StringLocalizer) | ApplicationService, AbpController | Localization |
AuthorizationService | ApplicationService, AbpController | Permission checks |
FeatureChecker | ApplicationService, AbpController | Feature availability |
DataFilter | All base classes | Data filtering (soft-delete, tenant) |
UnitOfWorkManager | ApplicationService, DomainService | Unit of work management |
LoggerFactory | All base classes | Create loggers |
Logger | All base classes | Logging (auto-created) |
LazyServiceProvider | All base classes | Lazy service resolution |
Useful methods from base classes:
CheckPolicyAsync() - Check permission and throw if not granted
IsGrantedAsync() - Check permission without throwing
Async Best Practices
- Use async all the way - never use
.Result or .Wait()
- All async methods should end with
Async suffix
- ABP automatically handles
CancellationToken in most cases (e.g., from HttpContext.RequestAborted)
- Only pass
CancellationToken explicitly when implementing custom cancellation logic
Time Handling
Never use DateTime.Now or DateTime.UtcNow directly. Use ABP's IClock service:
public class BookAppService : ApplicationService
{
public void DoSomething()
{
var now = Clock.Now;
}
}
public class MyService : ITransientDependency
{
private readonly IClock _clock;
public MyService(IClock clock) => _clock = clock;
public void DoSomething()
{
var now = _clock.Now;
}
}
Tip: Before injecting a service, check if it's already available as a property in your base classes.
Business Exceptions
Use BusinessException for domain rule violations with namespaced error codes:
throw new BusinessException("MyModule:BookNameAlreadyExists")
.WithData("Name", bookName);
Configure localization mapping:
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("MyModule", typeof(MyModuleResource));
});
Localization
- In base classes (
ApplicationService, AbpController, etc.): Use L["Key"] - this is the IStringLocalizer property
- In other services: Inject
IStringLocalizer<TResource>
- Always localize user-facing messages and exceptions
Localization file location: *.Domain.Shared/Localization/{ResourceName}/{lang}.json
{
"culture": "en",
"texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"BookName": "Book Name"
}
}
❌ Never Use (ABP Anti-Patterns)
| Don't Use | Use Instead |
|---|
| Minimal APIs | ABP Controllers or Auto API Controllers |
| MediatR | Application Services |
DbContext directly in App Services | IRepository<T> |
AddScoped/AddTransient/AddSingleton | ITransientDependency, ISingletonDependency |
DateTime.Now | IClock / Clock.Now |
| Custom UnitOfWork | ABP's IUnitOfWorkManager |
| Manual HTTP calls from UI | ABP client proxies (generate-proxy) |
| Hardcoded role checks | Permission-based authorization |
| Business logic in Controllers | Application Services |