by Joche Ojeda | Jan 8, 2026 | C#, XAF
Async/await in C# is often described as “non-blocking,” but that description hides an important detail:
await is not just about waiting — it is about where execution continues afterward.
Understanding that single idea explains:
- why deadlocks happen,
- why
ConfigureAwait(false) exists,
- and why it *reduces* damage without fixing the root cause.
This article is not just theory. It’s written because this exact class of problem showed up again in real production code during the first week of 2026 — and it took a context-level fix to resolve it.
The Hidden Mechanism: Context Capture
When you await a task, C# does two things:
- It pauses the current method until the awaited task completes.
- It captures the current execution context (if one exists) so the continuation can resume there.
That context might be:
- a UI thread (WPF, WinForms, MAUI),
- a request context (classic ASP.NET),
- or no special context at all (ASP.NET Core, console apps).
This default behavior is intentional. It allows code like this to work safely:
var data = await LoadAsync();
MyLabel.Text = data.Name; // UI-safe continuation
But that same mechanism becomes dangerous when async code is blocked synchronously.
The Root Problem: Blocking on Async
Deadlocks typically appear when async code is forced into a synchronous shape:
var result = GetDataAsync().Result; // or .Wait()
What happens next:
- The calling thread blocks, waiting for the async method to finish.
- The async method completes its awaited operation.
- The continuation tries to resume on the original context.
- That context is blocked.
- Nothing can proceed.
💥 Deadlock.
This is not an async bug. This is a context dependency cycle.
The Blast Radius Concept
Blocking on async is the explosion.
The blast radius is how much of the system is taken down with it.
Full blast (default await)
- Continuation *requires* the blocked context
- The async operation cannot complete
- The caller never unblocks
- Everything stops
Reduced blast (ConfigureAwait(false))
- Continuation does not require the original context
- It resumes on a thread pool thread
- The async operation completes
- The blocking call unblocks
The original mistake still exists — but the damage is contained.
The real fix is “don’t block on async,”
but ConfigureAwait(false) reduces the blast radius when someone does.
What ConfigureAwait(false) Actually Does
await SomeAsyncOperation().ConfigureAwait(false);
This tells the runtime:
“I don’t need to resume on the captured context. Continue wherever it’s safe to do so.”
Important clarifications:
- It does not make code faster by default
- It does not make code parallel
- It does not remove the need for proper async flow
- It only removes context dependency
Why This Matters in Real Code
Async code rarely exists in isolation.
A method often awaits another method, which awaits another:
await AAsync();
await BAsync();
await CAsync();
If any method in that chain requires a specific context, the entire chain becomes context-bound.
That is why:
- library code must be careful,
- deep infrastructure layers must avoid context assumptions,
- and UI layers must be explicit about where context is required.
When ConfigureAwait(false) Is the Right Tool
Use it when all of the following are true:
- The method does not interact with UI state
- The method does not depend on a request context
- The method is infrastructure, library, or backend logic
- The continuation does not care which thread resumes it
This is especially true for:
- NuGet packages
- shared libraries
- data access layers
- network and IO pipelines
What It Is Not
ConfigureAwait(false) is not:
- a fix for bad async usage
- a substitute for proper async flow
- a reason to block on tasks
- something to blindly apply everywhere
It is a damage-control tool, not a cure.
A Real Incident: When None of the Usual Fixes Worked
First week of 2026.
The first task I had with the programmers in my office was to investigate a problem in a trading block. The symptoms looked like a classic async issue: timing bugs, inconsistent behavior, and freezes that felt “await-shaped.”
We did what experienced .NET teams typically do when async gets weird:
- Reviewed the full async/await chain end-to-end
- Double-checked the source code carefully (everything looked fine)
- Tried the usual “tools people reach for” under pressure:
.Wait()
.GetAwaiter().GetResult()
- wrapping in
Task.Run(...)
- adding
ConfigureAwait(false)
- mixing combinations of those approaches
None of it reliably fixed the problem.
At that point it stopped being a “missing await” story. It became a “the model is right but reality disagrees” story.
One of the programmers, Daniel, and I went deeper. I found myself mentally replaying every async pattern I know — especially because I’ve written async-heavy code myself, including library work like SyncFramework, where I synchronize databases and deal with long-running operations.
That’s the moment where this mental model matters: it forces you to stop treating await like syntax and start treating it like mechanics.
The Actual Root Cause: It Was the Context
In the end, the culprit wasn’t which pattern we used — it was where the continuation was allowed to run.
This application was built on DevExpress XAF. In this environment, the “correct” continuation behavior is often tied to XAF’s own scheduling and application lifecycle rules. XAF provides a mechanism to run code in its synchronization context — for example using BlazorApplication.InvokeAsync, which ensures that continuations run where the framework expects.
Once we executed the problematic pipeline through XAF’s synchronization context, the issue was solved.
No clever pattern. No magical await. No extra parallelism.
Just: the right context.
And this is not unique to XAF. Similar ideas exist in:
- Windows Forms (UI thread affinity + SynchronizationContext)
- WPF (Dispatcher context)
- Any framework that requires work to resume on a specific thread/context
Why I’m Writing This
What I wanted from this experience is simple: don’t forget it.
Because what makes this kind of incident dangerous is that it looks like a normal async bug — and the internet is full of “four fixes” people cycle through:
- add/restore missing
await
- use
.Wait() / .Result
- wrap in
Task.Run()
- use
ConfigureAwait(false)
Sometimes those are relevant. Sometimes they’re harmful. And sometimes… they’re all beside the point.
In our case, the missing piece was framework context — and once you see that, you realize why the “blast radius” framing is so useful:
- Blocking is the explosion.
ConfigureAwait(false) contains damage when someone blocks.
- If a framework requires a specific synchronization context, the fix may be to supply the correct context explicitly.
That’s what happened here. And that’s why I’m capturing it as live knowledge, not just documentation.
The Mental Model to Keep
- Async bugs are often context bugs
- Blocking creates the explosion
- Context capture determines the blast radius
ConfigureAwait(false) limits the damage
- Proper async flow prevents the explosion entirely
- Frameworks may require their own synchronization context
- Correct async code can still fail in the wrong context
Async is not just about tasks. It’s about where your code is allowed to continue.
by Joche Ojeda | Oct 16, 2025 | Oqtane
OK, I’ve been wanting to write this article for a few days now, but I’ve been vibing a lot — writing tons of prototypes and working on my Oqtane research. This morning I got blocked by GitHub Copilot because I hit the rate limit, so I can’t use it for a few hours. I figured that’s a sign to take a break and write some articles instead.
Actually, I’m not really “writing” — I’m using the Windows dictation feature (Windows key + H). So right now, I’m just having coffee and talking to my computer. I’m still in El Salvador with my family, and it’s like 5:00 AM here. My mom probably thinks I’ve gone crazy because I’ve been talking to my computer a lot lately. Even when I’m coding, I use dictation instead of typing, because sometimes it’s just easier to express yourself when you talk. When you type, you tend to shorten things, but when you talk, you can go on forever, right?
Anyway, this article is about Oqtane, specifically something that’s been super useful for me — how to set up a silent installation. Usually, when you download the Oqtane source or use the templates to create a new project or solution, and then run the server project, you’ll see the setup wizard first. That’s where you configure the database, email, host password, default theme, and all that.
Since I’ve been doing tons of prototypes, I’ve seen that setup screen thousands of times per day. So I downloaded the Oqtane source and started digging through it — using Copilot to generate guides whenever I got stuck. Honestly, the best way to learn is always by looking at the source code. I learned that the hard way years ago with XAF from DevExpress — there was no documentation back then, so I had to figure everything out manually and even assemble the projects myself because they weren’t in one solution. With Oqtane, it’s way simpler: everything’s in one place, just a few main projects.
Now, when I run into a problem, I just open the source code and tell Copilot, “OK, this is what I want to do. Help me figure it out.” Sometimes it goes completely wrong (as all AI tools do), but sometimes it nails it and produces a really good guide.
So the guide below was generated with Copilot, and it’s been super useful. I’ve been using it a lot lately, and I think it’ll save you a ton of time if you’re doing automated deployment with Oqtane.
I don’t want to take more of your time, so here it goes — I hope it helps you as much as it helped me.
Oqtane Installation Configuration Guide
This guide explains the configuration options available in the appsettings.json file under the Installation section for automated installation and default site settings.
Overview
The Installation section in appsettings.json controls the automated installation process and default settings for new sites in Oqtane. These settings are particularly useful for:
- Automated installations – Deploy Oqtane without manual configuration
- Development environments – Quickly spin up new instances
- Multi-tenant deployments – Standardize new site creation
- CI/CD pipelines – Automate deployment processes
Configuration Structure
{
"Installation": {
"DefaultAlias": "",
"HostPassword": "",
"HostEmail": "",
"SiteTemplate": "",
"DefaultTheme": "",
"DefaultContainer": ""
}
}
| Key |
Purpose |
Required |
DefaultAlias |
Initial site URL(s) |
✅ |
HostPassword |
Super admin password |
✅ |
HostEmail |
Super admin email |
✅ |
SiteTemplate |
Initial site structure |
Optional |
DefaultTheme |
Site appearance |
Optional |
DefaultContainer |
Module wrapper style |
Optional |
SiteTemplate
A Site Template defines the initial structure and content of a new site, including pages, modules, folders, and navigation.
"SiteTemplate": "Oqtane.Infrastructure.SiteTemplates.DefaultSiteTemplate, Oqtane.Server"
Default options:
- DefaultSiteTemplate – Home, Privacy, example content
- EmptySiteTemplate – Minimal, clean slate
- AdminSiteTemplate – Internal use
If empty, Oqtane uses the default template automatically.
DefaultTheme
A Theme controls the visual appearance and layout of your site (page structure, navigation, header/footer, and styling).
"DefaultTheme": "Oqtane.Themes.OqtaneTheme.Default, Oqtane.Client"
Built-in themes:
- Oqtane Theme (default) – clean and responsive
- Blazor Theme – Blazor-branded styling
- Bootswatch variants – Cerulean, Cosmo, Darkly, Flatly, Lux, etc.
- Corporate Theme – business layout
If left blank, it defaults to the Oqtane Theme.
DefaultContainer
A Container is the wrapper around each module, controlling how titles, buttons, and borders look.
"DefaultContainer": "Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client"
Common containers:
- OqtaneTheme.Container – standard and responsive
- AdminContainer – management modules
- Theme-specific containers – match the chosen theme
Defaults automatically if left empty.
Example Configurations
Minimal Configuration
{
"Installation": {
"DefaultAlias": "localhost",
"HostPassword": "YourSecurePassword123!",
"HostEmail": "admin@example.com"
}
}
Custom Theme and Container
{
"Installation": {
"DefaultAlias": "localhost",
"HostPassword": "YourSecurePassword123!",
"HostEmail": "admin@example.com",
"SiteTemplate": "Oqtane.Infrastructure.SiteTemplates.DefaultSiteTemplate, Oqtane.Server",
"DefaultTheme": "Oqtane.Theme.Bootswatch.Flatly.Default, Oqtane.Theme.Bootswatch.Oqtane",
"DefaultContainer": "Oqtane.Theme.Bootswatch.Flatly.Container, Oqtane.Theme.Bootswatch.Oqtane"
}
}
Troubleshooting
- Settings ignored during installation: Ensure all required fields are filled (
DefaultAlias, HostPassword, HostEmail).
- Theme not found: Check assembly reference and type name.
- Container displays incorrectly: Use a container matching your theme.
- Site template creates no pages: Ensure your template returns valid page definitions.
Logs can be found in Logs/oqtane-log-YYYYMMDD.txt.
Best Practices
- Match your theme and container.
- Leave defaults empty unless customization is needed.
- Test in development first.
- Document any custom templates or themes.
- Use environment-specific appsettings (e.g.
appsettings.Development.json).
Summary
The Installation configuration in appsettings.json lets you fully automate your Oqtane setup.
- SiteTemplate: defines structure
- DefaultTheme: defines appearance
- DefaultContainer: defines module layout
Empty values use defaults, and you can override them for automation, branding, or custom scenarios.
by Joche Ojeda | Mar 2, 2025 | C#, System Theory
This past week, I have been working on a prototype for a wizard component. As you might know, in computer interfaces, wizard components (or multi-step forms) allow users to navigate through a finite number of steps or pages until they reach the end. Wizards are particularly useful because they don’t overwhelm users with too many choices at once, effectively minimizing the number of decisions a user needs to make at any specific moment.
The current prototype is created using XAF from DevExpress. If you follow this blog, you probably know that I’m a DevExpress MVP, and I wanted to use their tools to create this prototype.
I’ve built wizard components before, but mostly in a rush. Those previous implementations had the wizard logic hardcoded directly inside the UI components, with no separation between the UI and the underlying logic. While they worked, they were quite messy. This time, I wanted to take a more structured approach to creating a wizard component, so here are a few of my findings. Most of this might seem obvious, but sometimes it’s hard to see the forest for the trees when you’re sitting in front of the computer writing code.
Understanding the Core Concept: State Machines
To create an effective wizard component, you need to understand several underlying concepts. The idea of a wizard is actually rooted in system theory and computer science—it’s essentially an implementation of what’s called a state machine or finite state machine.
Theory of a State Machine
A state machine is the same as a finite state machine (FSM). Both terms refer to a computational model that describes a system existing in one of a finite number of states at any given time.
A state machine (or FSM) consists of:
- States: Distinct conditions the system can be in
- Transitions: Rules for moving between states
- Events/Inputs: Triggers that cause transitions
- Actions: Operations performed when entering/exiting states or during transitions
The term “finite” emphasizes that there’s a limited, countable number of possible states. This finite nature is crucial as it makes the system predictable and analyzable.
State machines come in several variants:
- Deterministic FSMs (one transition per input)
- Non-deterministic FSMs (multiple possible transitions per input)
- Mealy machines (outputs depend on state and input)
- Moore machines (outputs depend only on state)
They’re widely used in software development, hardware design, linguistics, and many other fields because they make complex behavior easier to visualize, implement, and debug. Common examples include traffic lights, UI workflows, network protocols, and parsers.
In practical usage, when someone refers to a “state machine,” they’re almost always talking about a finite state machine.
Implementing a Wizard State Machine
Here’s an implementation of a wizard state machine that separates the logic from the UI:
public class WizardStateMachineBase
{
readonly List<WizardPage> _pages;
int _currentIndex;
public WizardStateMachineBase(IEnumerable<WizardPage> pages)
{
_pages = pages.OrderBy(p => p.Index).ToList();
_currentIndex = 0;
}
public event EventHandler<StateTransitionEventArgs> StateTransition;
public WizardPage CurrentPage => _pages[_currentIndex];
public virtual bool MoveNext()
{
if (_currentIndex < _pages.Count - 1) { var args = new StateTransitionEventArgs(CurrentPage, _pages[_currentIndex + 1]); OnStateTransition(args); if (!args.Cancel) { _currentIndex++; return true; } } return false; } public virtual bool MovePrevious() { if (_currentIndex > 0)
{
var args = new StateTransitionEventArgs(CurrentPage, _pages[_currentIndex - 1]);
OnStateTransition(args);
if (!args.Cancel)
{
_currentIndex--;
return true;
}
}
return false;
}
protected virtual void OnStateTransition(StateTransitionEventArgs e)
{
StateTransition?.Invoke(this, e);
}
}
public class StateTransitionEventArgs : EventArgs
{
public WizardPage CurrentPage { get; }
public WizardPage NextPage { get; }
public bool Cancel { get; set; }
public StateTransitionEventArgs(WizardPage currentPage, WizardPage nextPage)
{
CurrentPage = currentPage;
NextPage = nextPage;
Cancel = false;
}
}
public class WizardPage
{
public int Index { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool IsRequired { get; set; } = true;
public bool IsCompleted { get; set; }
// Additional properties specific to your wizard implementation
public object Content { get; set; }
public WizardPage(int index, string title)
{
Index = index;
Title = title;
}
public virtual bool Validate()
{
// Default implementation assumes page is valid
// Override this method in derived classes to provide specific validation logic
return true;
}
}
Benefits of This Approach
As you can see, by defining a state machine, you significantly narrow down the implementation possibilities. You solve the problem of “too many parts to consider” – questions like “How do I start?”, “How do I control the state?”, “Should the state be in the UI or a separate class?”, and so on. These problems can become really complicated, especially if you don’t centralize the state control.
This simple implementation of a wizard state machine shows how to centralize control of the component’s state. By separating the state management from the UI components, we create a cleaner, more maintainable architecture.
The WizardStateMachineBase class manages the collection of pages and handles navigation between them, while the StateTransitionEventArgs class provides a mechanism to cancel transitions if needed (for example, if validation fails). The newly added WizardPage class encapsulates all the information needed for each step in the wizard.
What’s Next?
The next step will be to control how the visual components react to the state of the machine – essentially connecting our state machine to the UI layer. This will include handling the display of the current page content, updating navigation buttons (previous/next/finish), and possibly showing progress indicators. I’ll cover this UI integration in my next post.
By following this pattern, you can create wizard interfaces that are not only user-friendly but also maintainable and extensible from a development perspective.
Source Code
egarim/WizardStateMachineTest
About US
YouTube
https://www.youtube.com/c/JocheOjedaXAFXAMARINC
Our sites
https://www.bitframeworks.com
https://www.xari.io
https://www.xafers.training
Let’s discuss your XAF Support needs together! This 1-hour call/zoom will give you the opportunity to define the roadblocks in your current XAF solution
Schedule a meeting with us on this link
by Joche Ojeda | Feb 7, 2025 | Uncategorized
I recently had the privilege of conducting a training session in Cairo, Egypt, focusing on modern application development approaches. The session covered two key areas that are transforming how we build business applications: application frameworks and AI integration.
Streamlining Development with Application Frameworks
One of the highlights was demonstrating DevExpress’s eXpressApp Framework (XAF). The students were particularly impressed by how quickly we could build fully-functional Line of Business (LOB) applications. XAF’s approach eliminates much of the repetitive coding typically associated with business application development:
- Automatic CRUD operations
- Built-in security system
- Consistent UI across different platforms
- Rapid prototyping capabilities
Seamless Integration: XAF Meets Microsoft Semantic Kernel
What made this training unique was demonstrating how XAF’s capabilities extend into AI territory. We built the entire AI interface using XAF itself, showcasing how a traditional LOB framework can seamlessly incorporate advanced AI features. The audience, coming primarily from JavaScript backgrounds with Angular and React experience, was particularly impressed by how this approach simplified the integration of AI into business applications.
During the demonstrations, we explored practical implementations using Microsoft Semantic Kernel. The students were fascinated by practical demonstrations of:
- Natural language processing for document analysis
- Automated content generation for business documentation
- Intelligent decision support systems
- Context-aware data processing
Student Engagement and Outcomes
The response from the students, most of whom came from JavaScript development backgrounds, was overwhelmingly positive. As experienced frontend developers using Angular and React, they were initially skeptical about a different approach to application development. However, their enthusiasm peaked when they saw how these technologies could solve real business challenges they face daily. The combination of XAF’s rapid development capabilities and Semantic Kernel’s AI features, all integrated into a cohesive development experience, opened their eyes to new possibilities in application development.
Looking Forward
This training session in Cairo demonstrated the growing appetite for modern development approaches in the region. The intersection of efficient application frameworks and AI capabilities is proving to be a powerful combination for next-generation business applications.
And last, but not least, some pictures )))
by Joche Ojeda | Jan 22, 2025 | ADO.NET, C#, Data Synchronization, Database, DevExpress, XPO, XPO Database Replication
SyncFramework for XPO is a specialized implementation of our delta encoding synchronization library, designed specifically for DevExpress XPO users. It enables efficient data synchronization by tracking and transmitting only the changes between data versions, optimizing both bandwidth usage and processing time.
What’s New
- Base target framework updated to .NET 8.0
- Added compatibility with .NET 9.0
- Updated DevExpress XPO dependencies to 24.2.3
- Continued support for delta encoding synchronization
- Various performance improvements and bug fixes
Framework Compatibility
- Primary Target: .NET 8.0
- Additional Support: .NET 9.0
Our XPO implementation continues to serve the DevExpress community.
Key Features
- Seamless integration with DevExpress XPO
- Efficient delta-based synchronization
- Support for multiple database providers
- Cross-platform compatibility
- Easy integration with existing XPO and XAF applications
As always, if you own a license, you can compile the source code yourself from our GitHub repository. The framework maintains its commitment to providing reliable data synchronization for XPO applications.
Happy Delta Encoding! ?