MODULE 05 · LESSON 5.6
Host FabrCore inside a Blazor Server application
Run FabrCore and Blazor Interactive Server in one web application.
Lesson 26 of 86 · FabrCore 2.0
Overview
This is the server-integrated path: the browser uses a Blazor circuit, and server-side Surface services connect that principal's workspace to FabrCore. It does not require browser Orleans access or a custom browser WebSocket receive loop. Use the same Operations Desk agents as the mobile path.
The browser talks to the Blazor server
In this variant FabrCore runs inside the server-side web application. The browser's circuit talks to Blazor components; server-side Surface services connect the user's workspace to the runtime. You reuse the Operations Desk agent classes, but you do not implement the MAUI socket receive loop in the Razor page.
Register services and assets together
The example Program.cs wires the Host and Surface services. App.razor supplies the matching assets and interactive component setup, while Routes.razor selects pages. Missing one layer can produce an application that starts successfully but displays a noninteractive chat panel. The development principal is a sample convenience; a deployed circuit must use the actual signed-in user's scope.
Run the integrated Blazor host
Download the Operations Desk source. The README lists project setup, package prerequisites and local ports.
- Create a Blazor Web App with Interactive Server and reference FabrCore.Host, the agents library and FabrCore.Surface. Register Razor components, FabrCore and Surface services in Program.cs.
- Add Surface styles/assets and the documented route extension if using /surface. Ensure the page/layout hosting SurfaceChatLink has an interactive render mode; registering services alone does not make a static Razor component interactive.
- Map the authenticated user to ISurfacePrincipalContextProvider and a scoped SurfaceWorkspaceService. Keep development fallback identity restricted to the development environment.
builder.AddFabrCoreSurfaceFromConfig("fabrcore-surface.json", "crm-demo");
builder.Services.AddFabrCoreSurfaceComponents();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddFabrCoreSurfaceRoutes();
using FabrCore.Host;
using FabrCore.Surface;
using FabrCore.Surface.Services;
using OperationsDesk.Web.Components;
var builder = WebApplication.CreateBuilder(args);
if (!builder.Environment.IsDevelopment())
throw new InvalidOperationException("Configure authenticated Surface principal identity before deploying this development sample.");
builder.WebHost.UseUrls("http://localhost:5099");
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.AddFabrCoreServer();
builder.AddFabrCoreSurface(options => {
options.DevelopmentFallbackPrincipalId = "dev";
options.CommandCenterChatDeliveryMode = SurfaceChatDeliveryMode.RequestResponse;
});
builder.Services.AddFabrCoreSurfaceComponents();
var app = builder.Build();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseFabrCoreServer();
app.MapRazorComponents<App>().AddInteractiveServerRenderMode().AddFabrCoreSurfaceRoutes();
app.Run();
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><base href="/" />
<link href="_content/FabrCore.Surface/surface.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet" />
<HeadOutlet @rendermode="InteractiveServer" /></head><body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body></html>
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="[typeof(SurfaceCommandCenter).Assembly]">
<Found Context="routeData"><RouteView RouteData="routeData" /><FocusOnNavigate RouteData="routeData" Selector="h1" /></Found>
<NotFound><h1>Page not found</h1></NotFound>
</Router>
Open the page and send the first message
- Stop the standalone sample before starting OperationsDesk.Web, because their default Localhost Orleans ports overlap. Run the Web sample's guide launch profile and browse http://localhost:5099.
- Open the request page, launch chat and create the example agent if prompted. Send “Blazor check SR-1042” and expect the echo reply in the panel.
- Reload the page and compare the workspace's agent/history state. Inspect server logs if the panel opens but cannot create or message an instance.
This verifies the integrated server path. It does not require exposing Orleans to the browser or adding a custom browser WebSocket client.
If the result is different
Check the Blazor web script, render mode and Surface registration if controls render but do nothing. Do not register a singleton transcript shared across users.
Go deeper
Explore the related documentation.