Writing Your Own Roslyn Analyzers for .NET MAUI
🧠 Writing Your Own Roslyn Analyzers for .NET MAUI
Enforcing Mobile Architecture, Performance, and UI Standards
As .NET MAUI applications grow, maintaining consistency across:
- 📱 UI layers
- ⚡ Performance-sensitive code
- 🧩 MVVM boundaries
- 🔄 Platform-specific implementations …becomes increasingly difficult. Code reviews help—but they don’t scale perfectly. That’s where Roslyn Analyzers become incredibly powerful. Instead of relying on humans to constantly detect issues, you can create custom compile-time rules that automatically enforce your MAUI architecture and coding standards. In this guide, we’ll go beyond generic analyzers and focus specifically on:
- 📱 MAUI best practices
- 🧩 MVVM enforcement
- ⚡ Performance rules
- 🚫 UI thread misuse
- 🔄 Platform-specific patterns
- 🏗️ Architecture validation
🧠 Why Roslyn Analyzers Matter More in MAUI
Mobile apps introduce challenges traditional desktop/web apps don’t have:
| Mobile Concern | Why It Matters |
|---|---|
| UI thread blocking ⚠️ | Causes freezes and ANRs |
| Memory leaks 🧠 | Mobile devices are constrained |
| Platform-specific APIs 📱 | Hard to enforce consistency |
| MVVM separation 🧩 | Easy to accidentally break |
| Performance-sensitive rendering ⚡ | Critical for UX |
Roslyn lets you enforce these rules before the app even runs.
🚀 Real MAUI Problems Roslyn Can Solve
Instead of generic naming rules, imagine analyzers that detect:
- ❌
Task.Resulton UI thread - ❌ Direct API calls from Views
- ❌ Massive code-behind files
- ❌ Missing
MainThread.InvokeOnMainThreadAsync - ❌ Disallowed platform APIs in shared projects
- ❌ Non-disposable image resources
- ❌ UI controls created inside loops
- ❌ Forbidden navigation patterns This is where analyzers become architectural superpowers.
🧩 Roslyn Architecture Refresher
Roslyn analyzers can:
- Analyze syntax trees 🌳
- Analyze symbols 🧠
- Emit diagnostics ⚠️
- Offer automatic fixes 🛠️
📊 Analyzer vs MAUI-Specific Use Cases
| Analyzer Type | MAUI Example |
|---|---|
| Naming | Enforce ViewModel suffix |
| Performance | Detect blocking calls on UI thread |
| Architecture | Prevent Views from accessing repositories |
| Platform Rules | Restrict #if ANDROID usage |
| Threading | Ensure UI updates occur on MainThread |
⚙️ Getting Started
📦 Create Analyzer Project
dotnet new analyzer -n MauiArchitectureAnalyzer
🧩 Example #1 — Enforce ViewModel Naming
In MAUI MVVM apps:
LoginViewModel ✅
LoginVM ❌
🔧 Diagnostic Definition
private static readonly DiagnosticDescriptor Rule = new(
id: "MAUI001",
title: "ViewModels must end with ViewModel",
messageFormat: "Class '{0}' should end with 'ViewModel'",
category: "Architecture",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
🔎 Register Analyzer
context.RegisterSymbolAction(
AnalyzeSymbol,
SymbolKind.NamedType);
🧠 Analyze Class
private void AnalyzeSymbol(SymbolAnalysisContext context)
{
var symbol = (INamedTypeSymbol)context.Symbol;
if (symbol.Name.Contains("VM"))
{
var diagnostic = Diagnostic.Create(
Rule,
symbol.Locations[0],
symbol.Name);
context.ReportDiagnostic(diagnostic);
}
}
⚡ Example #2 — Detect Blocking Calls on UI Thread
One of the most common MAUI performance issues:
var result = myTask.Result; // ❌
This can freeze the UI.
🧠 Analyzer Logic
context.RegisterSyntaxNodeAction(
AnalyzeMemberAccess,
SyntaxKind.SimpleMemberAccessExpression);
🔍 Detection
private void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context)
{
var memberAccess = (MemberAccessExpressionSyntax)context.Node;
if (memberAccess.Name.Identifier.Text == "Result")
{
var diagnostic = Diagnostic.Create(
Rule,
memberAccess.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
🧩 Example #3 — Prevent Repository Access from Views
Bad MAUI pattern:
public partial class MainPage : ContentPage
{
private readonly UserRepository _repository;
}
Views should talk to ViewModels—not repositories.
🧠 Architectural Analyzer
if (classSymbol.BaseType?.Name == "ContentPage")
{
foreach (var field in classSymbol.GetMembers().OfType<IFieldSymbol>())
{
if (field.Type.Name.EndsWith("Repository"))
{
context.ReportDiagnostic(...);
}
}
}
📱 Example #4 — Enforce MainThread Usage
Incorrect:
MyLabel.Text = "Updated";
from background threads.
Correct:
await MainThread.InvokeOnMainThreadAsync(() =>
{
MyLabel.Text = "Updated";
});
You can build analyzers to detect unsafe UI access.
🛠️ Code Fixes for MAUI
Roslyn becomes even more powerful with auto-fixes.
Example Fix
Convert:
myTask.Result
into:
await myTask
🧪 Unit Testing MAUI Analyzers
Testing analyzers is critical.
Example
[TestMethod]
public async Task DetectsBlockingTaskResult()
{
var code = """
public async Task Load()
{
var x = task.Result;
}
""";
var expected = new DiagnosticResult("MAUI002");
await VerifyAnalyzerAsync(code, expected);
}
📦 Shipping Your Analyzer Across MAUI Solutions
Option 1 — NuGet Package
Perfect for:
- Enterprise MAUI apps
- Internal company standards
- Multi-team consistency
Option 2 — CI/CD Enforcement
Analyzers work beautifully in:
- GitHub Actions ⚡
- Azure DevOps 🚀
- Pull request validation 🔍
🧠 Advanced MAUI Analyzer Ideas
| Rule | Purpose |
|---|---|
| Detect large XAML pages | UI maintainability |
| Detect unawaited Tasks | Prevent async bugs |
| Prevent direct SQLite access from UI | Architecture |
| Enforce DI registration | Scalability |
| Detect excessive BindableProperties | Performance |
| Restrict GraphicsView misuse | Rendering optimization |
⚖️ Why This Is Powerful for MAUI Teams
| Traditional Review | Roslyn Analyzer |
|---|---|
| Human-dependent | Automated |
| Inconsistent | Deterministic |
| Happens after coding | Happens while coding |
| Easy to miss issues | Immediate feedback |
🚀 Performance Benefits
Analyzers can help catch:
- UI freezes ⚠️
- Over-rendering 🎨
- Threading violations 🔄
- Memory leaks 🧠
- Architecture drift 🏗️ …before they hit production.
🔗 Reference Links
- https://github.com/dotnet/roslyn
- https://learn.microsoft.com/dotnet/maui/
- https://learn.microsoft.com/dotnet/fundamentals/code-analysis/overview
🧠 Key Takeaways
- Roslyn analyzers are incredibly valuable for MAUI apps 📱
- Mobile-specific problems are perfect candidates for compile-time analysis ⚡
- You can enforce architecture, performance, and threading rules automatically 🧩
- Custom analyzers scale much better than manual PR reviews 🚀
🔚 Final Thoughts
In large MAUI applications, maintaining consistency manually becomes almost impossible over time. Roslyn analyzers allow you to transform:
- tribal knowledge
- PR comments
- architecture documents …into real, enforceable compiler rules.
And that changes how teams build mobile apps.
Instead of hoping developers follow best practices…
you can make the IDE enforce them automatically. ⚡
