Advantages & Disadvantages
An honest assessment of Metalama's strengths, weaknesses, and when it's the right (or wrong) choice.
Advantages
1. Clean, Readable Source Code
The primary benefit. Cross-cutting concerns are declared as attributes, keeping business logic uncluttered:
// Without AOP: 30+ lines of infrastructure code per method
// With AOP: Business logic stands out clearly
[Authorize]
[Log]
[Cache(AbsoluteExpirationSeconds = 300)]
[Retry(MaxAttempts = 3)]
public async Task<Recipe> GetRecipeAsync(int id)
{
return await _repository.GetByIdAsync(id);
}
2. Zero Runtime Overhead
Unlike runtime proxy-based frameworks, Metalama generates the infrastructure code at compile time. The output assembly contains only normal method calls — no reflection, no dynamic dispatch, no proxy overhead.
Comparison:
| Framework | Mechanism | Overhead |
|---|---|---|
| Metalama | Compile-time code generation | None |
| Castle DynamicProxy | Runtime proxy creation | Proxy instantiation + virtual dispatch |
| DispatchProxy | Runtime proxy creation | Same as Castle |
| Reflection-based | Runtime reflection | Significant (reflection is slow) |
3. Compile-Time Error Detection
Mistakes are caught during compilation, not at runtime:
[Cache] // ❌ Compile error: "Cannot cache void methods"
public void ProcessOrder(Order order) { }
[NotifyPropertyChanged] // ❌ Compile error: "Class must be partial"
public class ViewModel { }
4. IDE Integration
Metalama integrates with Visual Studio and Rider:
- Code lens shows which aspects are applied
- Quick fixes suggest aspect application
- Warnings and errors appear in the IDE
- Generated code is browsable in
obj/.../metalama/
5. Debuggable Generated Code
Unlike IL rewriting (PostSharp pre-2023), Metalama generates C# source code that you can:
- Read and understand
- Set breakpoints in
- Step through with the debugger
- Diff between versions
6. Separation of Concerns (True Modularity)
Each cross-cutting concern lives in one place:
- Logging logic →
LogAttribute.cs - Retry logic →
RetryAttribute.cs - Audit logic →
AuditAttribute.cs
Changing the logging format means modifying one file, not 200 methods.
7. Consistency
When logging is implemented in one aspect, every method that uses [Log] behaves identically. No risk of:
- Different log formats in different methods
- Forgotten try/catch blocks
- Inconsistent error handling
8. Composability
Aspects can be stacked without conflicts:
[Log] // ← These don't know about each other
[Retry] // ← But they compose naturally
[Cache] // ← Through the meta.Proceed() pipeline
public Result GetData() { }
9. Contract Inheritance
Validation contracts are inherited from interfaces, ensuring consistency across implementations without repetition.
10. Testability
Aspects can be tested independently:
- Snapshot tests verify code generation
- Run-time tests verify behavior
- No need to test infrastructure code in every service
Disadvantages
1. Learning Curve
Metalama introduces several new concepts:
- T# template language (compile-time vs. runtime code)
meta.*API- Code model interfaces (IMethod, IType, etc.)
- Compile-time vs. run-time mental model
Mitigation: This guide exists to flatten the learning curve.
2. Build-Time Complexity
Metalama adds a step to the compilation pipeline:
- Slower builds: Compile time increases (typically 10-30% for large projects)
- Build dependencies: Metalama NuGet package must be available
- CI/CD: Build servers need Metalama installed
- Version sensitivity: Metalama version must match across projects
3. Debugging Indirection
While generated code is debuggable, the indirection can be confusing:
- Source code doesn't match what runs
- Breakpoints must be set in transformed code
- Stack traces show generated method names
meta.DebugBreak()vsDebugger.Break()confusion
4. Hidden Behavior ("Magic")
Aspects add behavior that isn't visible at the call site:
service.GetData(); // ← Is this cached? Logged? Retried? You can't tell.
This can make code harder to reason about for developers unfamiliar with the aspects.
Mitigation: Good naming conventions ([Cache], [Retry]) and team documentation.
5. Vendor Lock-In
Metalama is the only mature compile-time AOP framework for modern .NET:
- Migrating away requires rewriting all aspects as manual code
- PostSharp (predecessor) is discontinued for new development
- No open-source alternative with similar features
Mitigation: Aspects are well-encapsulated. If you need to remove Metalama, you can mechanically inline each aspect's behavior into the target methods.
6. Limited to C#
Metalama only works with C#:
- No F# support
- No VB.NET support
- C++ interop projects unaffected (aspects only apply to C# code)
7. Partial Class Requirement
TypeAspect (which introduces members) requires target classes to be partial:
// Must be 'partial' to receive introduced members
[NotifyPropertyChanged]
public partial class ViewModel { }
This can feel intrusive, especially for existing codebases.
8. Service Locator Pattern (GST-Specific)
Because aspects are compile-time artifacts, they cannot use constructor injection. The GST framework uses AspectServiceLocator (a static service locator), which:
- Is considered an anti-pattern in DI communities
- Creates a hidden dependency on service initialization
- Requires explicit
InitializeAspects()call at startup
Mitigation: This is a Metalama limitation, not a design choice. The Metalama team is exploring better DI integration. The Metalama.Extensions.DependencyInjection package provides some alternatives.
9. Compile-Time Constraints
Template code must be compile-time safe:
- Cannot use
nameof()for introduced members - Cannot reference run-time-only APIs in compile-time code
foreachover parameters is unrolled (can produce large methods)- Some C# patterns don't work in templates
10. Licensing
Metalama has a commercial license model:
- Free: Metalama Free for open-source and small projects
- Essential: Paid license for commercial projects (some limitations)
- Ultimate: Full feature set (higher cost)
Check metalama.net/pricing for current details.
Comparison with Alternatives
Metalama vs. PostSharp
| Aspect | Metalama | PostSharp |
|---|---|---|
| Generation | Next-gen (source transformation) | Legacy (IL rewriting) |
| Source visibility | Generated C# code is readable | IL is opaque |
| Debugging | Standard C# debugging | Special debugging tools needed |
| Performance | Same or better | Good |
| Ecosystem | Growing | Mature but declining |
| Migration | Migration guide available | N/A |
| Status | Active development | Maintenance mode |
Metalama vs. Castle DynamicProxy
| Aspect | Metalama | Castle DynamicProxy |
|---|---|---|
| Weaving | Compile-time | Runtime (proxy) |
| Performance | Zero overhead | Proxy overhead |
| Scope | Any method/property/field | Interface/virtual methods only |
| Setup | NuGet + attributes | DI container registration |
| Debugging | Generated C# | Runtime proxy stack |
| Error detection | Compile-time | Runtime |
Metalama vs. C# Source Generators
| Aspect | Metalama | Source Generators |
|---|---|---|
| Can modify existing code | ✅ Yes | ❌ No (only add new code) |
| Template language | T# (high-level) | Roslyn syntax tree (low-level) |
| Base classes | Rich (OverrideMethodAspect, etc.) | None (DIY) |
| Composability | Built-in (aspect pipeline) | Manual |
| Learning curve | Medium | High (Roslyn API) |
| IDE support | Good | Good |
Metalama vs. Manual Code / No AOP
| Aspect | Metalama | Manual Code |
|---|---|---|
| Readability | Clean (attributes) | Cluttered (interleaved concerns) |
| Consistency | Guaranteed | Developer-dependent |
| Maintenance | Change in one place | Change in N places |
| Learning curve | Initial investment | None |
| Build complexity | Additional step | None |
| Debugging | Indirect | Direct |
When to Use Metalama
Good Use Cases ✅
| Scenario | Why |
|---|---|
| Many methods with same infrastructure | DRY — define once, apply everywhere |
| Compliance requirements (FDA, SOX) | Consistent, auditable, certified aspect behavior |
| MVVM applications (WPF, MAUI) | INotifyPropertyChanged boilerplate elimination |
| Library/framework development | Enforce patterns on consumers |
| Large teams | Consistency despite varying developer skill levels |
| Long-lived codebases | Centralized concern management reduces maintenance |
Poor Use Cases ❌
| Scenario | Why |
|---|---|
| Small projects (< 10 classes) | Overhead not justified |
| Prototypes / throwaway code | Over-engineering |
| Single-developer scripts | No consistency benefit |
| Performance-critical hot paths | Though overhead is minimal, extra generated code adds complexity |
| Teams unfamiliar with AOP (without training) | "Magic" code causes confusion |
| Non-C# projects | Not supported |
Summary
The Value Proposition
Metalama is most valuable when you have:
- ≥ 10 methods that share the same cross-cutting concern
- A need for consistency (compliance, team standards)
- Cross-cutting concerns that change over time (centralized modification)
- A team willing to invest in learning the framework
The Cost
- Learning curve for the team
- Build-time increase
- Debugging indirection
- Vendor dependency
Bottom Line
Metalama trades initial learning investment for long-term maintainability. For codebases with significant cross-cutting concerns (like the GST framework), the trade-off is overwhelmingly positive.
Next: Best Practices — Design principles and common pitfalls.