Overview
When a service entry is determined to execute locally (IsLocal = true), the framework uses ILocalExecutor to invoke the actual business method. Key characteristics:
- A server-side filter pipeline runs before and after the business method call
- Filters support both async and sync interfaces; the framework drives the pipeline through a state machine (similar to ASP.NET Core's ActionFilter mechanism)
- Built-in filter types: authorization, action, exception, and result filters
DefaultLocalExecutor — Local Execution Entry
public class DefaultLocalExecutor : ILocalExecutor
{
private readonly IServerLocalInvokerFactory _serverLocalInvokerFactory;
public async Task<object> Execute(ServiceEntry serviceEntry, object[] parameters, string? serviceKey = null)
{
// 1. Resolve implementation instance by serviceKey (supports multi-impl routing)
var instance = EngineContext.Current.ResolveServiceInstance(serviceKey, serviceEntry.ServiceType);
// 2. Build ServiceEntryContext (wraps ServiceEntry, parameters, instance)
// 3. Create LocalInvoker with filter chain from factory
var localInvoker = _serverLocalInvokerFactory.CreateInvoker(
new ServiceEntryContext(serviceEntry, parameters, serviceKey, instance));
// 4. Run filter pipeline + business method
await localInvoker.InvokeAsync().ConfigureAwait(false);
// 5. Return result
return localInvoker.Result;
}
}
ServiceKey Multi-Implementation Routing
ResolveServiceInstance(serviceKey, serviceType) resolves the correct implementation from the IoC container based on serviceKey. When multiple implementations of the same interface exist (e.g., different tenant implementations), each is registered with a different [InjectNamed] key matching the ServiceKey.
ServiceEntryContext — Call Context
ServiceEntryContext is the "session" object that flows through the entire filter pipeline:
| Property | Type | Description |
|---|---|---|
ServiceEntry | ServiceEntry | Current service entry metadata (routes, parameter descriptors, governance options) |
Parameters | object[] | Resolved parameter array (matching method signature order) |
ServiceKey | string? | Service key for this call (multi-impl routing) |
Instance | object | The business implementation instance |
Result | object | Return value after execution |
IServiceEntryContextAccessor (backed by AsyncLocal<T>) allows filters and business code to access the current call context from anywhere in the call stack.
Server-Side Filter Types
1. Authorization Filters
public interface IAsyncServerAuthorizationFilter : IServerFilterMetadata
{
Task OnAuthorizationAsync(ServerAuthorizationFilterContext context);
}
- Execute first, before all Action filters
- Setting
context.Resultto a non-null value short-circuits the entire pipeline - Built-in authorization filter validates
[Authorize]and JWT claims
2. Action Filters (IServerFilter / IAsyncServerFilter)
public interface IAsyncServerFilter : IServerFilterMetadata
{
Task OnActionExecutionAsync(ServerInvokeExecutingContext context, ServerExecutionDelegate next);
}
- Execute before (
OnActionExecuting) and after (OnActionExecuted) the business method - Can short-circuit by not calling
next() - Use
context.ExceptioninOnActionExecutedto inspect exceptions
3. Exception Filters
Catch unhandled exceptions from Action filters or the business method. Setting context.ExceptionHandled = true prevents the exception from propagating.
4. Result Filters
Execute before (OnResultExecuting) and after (OnResultExecuted) the result is written. Can modify or replace context.Result.
5. IAlwaysRunServerResultFilter
Always executes regardless of short-circuiting — useful for cleanup operations that must run.
Filter State Machine
The LocalInvoker drives execution through a state machine with these states:
ActionBegin
│ → Run Authorization filters (can short-circuit)
▼
AuthorizationBegin
│ → Run Action filters (OnActionExecuting)
▼
ActionInside
│ → ObjectMethodExecutor invokes the business method
▼
ResultBegin
│ → Run Result filters (OnResultExecuting → OnResultExecuted)
▼
InvokeEnd
│ → Execution complete, result available in LocalInvoker.Result
On exception, the state machine switches to Exception filter execution before completing.
ObjectMethodExecutor
ObjectMethodExecutor uniformly handles all .NET method return types:
| Return Type | Handling |
|---|---|
void | Executes synchronously, result is null |
T (non-Task) | Executes synchronously, wraps result |
Task | await task |
Task<T> | await task, extracts T |
ValueTask | await valueTask.AsTask() |
ValueTask<T> | await valueTask.AsTask(), extracts T |
This ensures business methods can use any async return style without impacting the framework's execution pipeline.
