Quick Review
- The Core Idea: Solving a Classic Web Dilemma
- Blazor’s “Auto” mode was created to fix the fundamental trade-off between its two original hosting models.
- Blazor Server: Offered a super-fast initial load but could feel laggy because every click required a round-trip to the server.1
- Blazor WebAssembly (WASM): Provided a rich, client-side experience but often forced users to endure a long initial download.3
- “Auto” mode’s primary goal is to give users the best of both: the instant startup of Server and the fluid interactivity of WASM.5
- How It Works: A Phased Approach to Interactivity
- First Visit: The application uses the Blazor Server model to make the page interactive almost instantly. While the user is interacting with the app, the framework quietly downloads the necessary WebAssembly files in the background.3
- Subsequent Visits: Once the WASM files are cached in the browser, the app automatically switches to using Blazor WebAssembly. This moves the processing to the user’s machine, resulting in a faster, more responsive experience without needing a constant server connection.3
- The Two-Project Structure: A Necessary Division
- The split into a Server project and a
.Client
project isn’t just for organization; it’s a hard architectural requirement. - The Server Project: This is your trusted backend. It’s a standard ASP.NET Core application that handles the initial web requests, hosts your API endpoints, and is the only place where you should be directly accessing databases or handling sensitive secrets like connection strings.9
- The
.Client
Project: This is the part of your app that gets packaged up and sent to the user’s browser. It’s the mandatory home for any component that needs to run interactively on the client using@rendermode InteractiveAuto
or@rendermode InteractiveWebAssembly
.8
- The split into a Server project and a
- Component Lifecycle and the “Flicker” Problem
- Prerendering: To feel fast, interactive components are first rendered as static HTML on the server. This gets content to the screen quickly.11
- The Problem: After this static version appears, the component initializes again in its interactive mode (either Server or WASM). Because state isn’t automatically carried over, the component often has to fetch its data a second time, which can cause a jarring “flicker” in the UI.13
- The Solution: The framework provides a specific tool,
PersistentComponentState
, to solve this. You use it to save the data from the first render and pass it along to the second, avoiding the redundant data fetch and the UI flicker.12
- The Golden Rule for Placing Components
- It’s simple: if a component is meant to be interactive in the browser, it must live in the
.Client
project.8 Only the code in this project is actually sent to the browser, so a component in the Server project can never run on WebAssembly.10
- It’s simple: if a component is meant to be interactive in the browser, it must live in the
- Smart Data Access for a Hybrid World
- An “Auto” component needs to be able to get data whether it’s running on the server or in the browser.
- The Pattern: The best practice is to abstract your data logic.
- Define an interface (like
IProductService
) in a shared project. - Write a server-side implementation in the Server project that talks directly to the database.
- Write a client-side implementation in the Client project that calls your API using
HttpClient
. - By injecting the interface into your component, the right implementation is automatically provided depending on where the code is running.10
- Define an interface (like
- Key Details to Remember
HttpClient
Registration: You have to registerHttpClient
services in both the Server and ClientProgram.cs
files. This is a common stumbling block that causes errors during the prerendering step.13- Secure Authentication: For handling user logins securely, the recommended approach is the Backend-for-Frontend (BFF) pattern. This keeps sensitive tokens on the server and uses secure cookies for the client, which is much safer than handling tokens directly in the browser.16
Introduction
The journey of the Blazor framework has been one of refinement. It began with a clear choice between two distinct paths: Blazor Server and Blazor WebAssembly. This initial split has since evolved into the unified Blazor Web App framework in.NET 8, a change driven by the real-world trade-offs developers faced. Blazor Server delivered an impressively quick startup and a lightweight client, but every user interaction came with network latency and a heavy resource burden on the server.1 On the other hand, Blazor WebAssembly offered the raw power of a client-side single-page application (SPA) built on an open standard, but often at the cost of a hefty initial download that could frustrate users and hurt engagement.3
The centerpiece of this new, unified approach is the Interactive Auto render mode, simply known as “Auto” mode. It’s engineered to deliver a “best of both worlds” experience by cleverly blending the strengths of its predecessors.5 This report offers a deep architectural look at the Blazor “Auto” render mode. We’ll break down the reasoning behind its dual-project structure, trace the intricate lifecycle of a component as it renders, and lay out clear, practical patterns for organizing components, accessing data, and designing APIs. The goal is to give professional developers and architects the foundational knowledge needed to truly master this powerful, yet nuanced, framework.
Section 1: The Architectural Rationale for the Dual-Project Model
1.1 The Core Problem: Optimizing Time-to-Interactivity (TTI)
In modern web development, the race is to minimize not just how long a page takes to appear, but how quickly a user can actually interact with it—a key metric called Time-to-Interactivity (TTI). The original Blazor models presented a classic TTI challenge.
A pure WebAssembly app makes the user wait while the browser downloads, parses, and fires up the.NET runtime and all the application’s code.3 This initial payload can be quite large, leading to a “loading…” screen that can be a deal-breaker for users, especially on slower connections.4 In stark contrast, a pure Blazor Server app can paint the initial HTML almost instantly. The catch is that every button click, every keystroke, has to travel over the network to the server through a SignalR connection, get processed, and have the UI changes sent back. This constant back-and-forth can introduce noticeable lag, making the app feel sluggish, and it puts a continuous strain on the server, which has to maintain a live connection for every active user.1
1.2 “Auto” Mode as the Solution: A Phased Approach to Rendering
“Auto” mode is a sophisticated and direct answer to this TTI problem. It uses a phased rendering strategy that intelligently manages the user experience, delivering the right model at the right time.
Phase 1: Instant Interactivity via Blazor Server
When a user first lands on a page with an “Auto” mode component, the framework defaults to using Interactive Server-Side Rendering.3 It sends a static version of the page for a quick initial display, then immediately opens a lightweight SignalR connection. This makes the component fully interactive almost instantly, powered by the Blazor Server model.1 At the same time, completely hidden from the user, the framework starts downloading the.NET WebAssembly runtime and the client-side parts of the application in the background.8
Phase 2: Seamless Transition to Client-Side Power
Once that WebAssembly payload is downloaded and cached by the browser, the framework’s strategy changes. On the user’s next visit, or when they navigate to another page that uses the same component, “Auto” mode will now use those cached assets to run the component using Interactive WebAssembly.3 This gets rid of the need for a persistent SignalR connection for UI events and shifts the workload to the client, delivering a true SPA experience with near-native responsiveness and a lighter load on the server.2
It’s important to grasp that in.NET 8, this isn’t a live switch for a component that’s already on the page. The rendering decision is made right before a component is about to be rendered, based mainly on whether the WebAssembly resources are already cached.3 The framework also tries to be consistent; it prefers to pick a render mode that matches other interactive components already on the page to avoid the complexity of running two different runtimes (Server and WASM) that can’t share their state.3
This carefully timed orchestration is the key innovation of “Auto” mode. It effectively separates the user’s initial interactive experience from the high upfront cost of the WASM download. The Blazor Server model is cleverly used as a bridge to hide the TTI penalty of the WebAssembly model. What was once a blocking download in a pure WASM app becomes a non-blocking background process from the user’s point of view, letting them use a fully working application while the more powerful client-side runtime gets ready for future use.
1.3 Why Two Projects Are Non-Negotiable
This phased, dual-execution model creates a fundamental architectural need: a clean separation between code that can run in the browser’s sandboxed WebAssembly environment and code that must only ever run on the trusted server. This separation is physically enforced by the dual-project structure.
The .Client
project is the code destined for the browser. It’s the distributable package, the payload of components and logic that will be downloaded and run via WebAssembly.9 The main Server project (often just
YourApp
) serves as the host and the secure backend. It’s responsible for handling the first request, serving the client payload, and doing things that are impossible or unsafe in a browser, like talking directly to a database or managing application secrets.9
So, the two-project structure isn’t a matter of style or convenience; it’s a rigid architectural boundary required by the dual-execution nature of “Auto” mode. Choosing this mode fundamentally shifts your application from a single-process model (all-server or all-client) to a distributed system, even if it’s deployed as one unit. This has major implications for everything from state management and data access to security. The complexity developers sometimes feel is a direct result of this paradigm shift.3 A component marked
InteractiveAuto
has to be written to work correctly in two completely different environments, which turns what used to be optional best practices, like using data service interfaces, into mandatory architectural patterns.10 Picking “Auto” mode isn’t just selecting a render option; it’s committing to a more advanced, distributed application architecture.
Section 2: Anatomy of the Server and Client Projects
2.1 The Server Project: The ASP.NET Core Host and Origin
The main Server project is the application’s command center. It operates as a complete ASP.NET Core application, acting as the entry point and the trusted backend for the entire system.8
Its list of duties is extensive:
- Hosting and Initial Request Handling: It listens for all incoming HTTP requests, serves the application’s initial HTML shell (often defined in a root file like
_Host.cshtml
), and kicks off the Blazor rendering process.8 - Server-Side Rendering (SSR): This is where all server-based rendering happens. This includes static SSR for non-interactive parts, prerendering for interactive components, and the real-time UI updates for components running in Blazor Server mode. In short, it’s where the HTML sent to the client is born.9
- API Endpoint Host: This project is the exclusive home for all your web API endpoints. Whether you prefer Minimal APIs or traditional MVC Controllers, these endpoints define the data contract that your client-side application will use.10
- Direct Resource Access: As a trusted backend, the Server project can safely and directly communicate with databases (using tools like Entity Framework Core), the file system, and other server-side services. It’s the only place where sensitive data like connection strings or API keys should ever be stored and accessed.7
- Configuration and Services (
Program.cs
): The server’sProgram.cs
file is where you configure the ASP.NET Core host and its dependency injection (DI) container. It registers services needed for server-side operations, sets up authentication, and builds the request pipeline. Crucially, it includes the calls toAddRazorComponents()
,AddInteractiveServerComponents()
, andMapRazorComponents()
that bring Blazor’s functionality to life.8
Typically, the Server project holds the main Program.cs
, your API definitions, server-specific services (like a DbContext
), configuration files, and any Razor components that are meant to run only in static or server-interactive modes.9
2.2 The Client Project: The Downloadable WebAssembly Payload
The .Client
project is a separate, self-contained unit. Its compiled output—the application’s .dll
files and the.NET WebAssembly runtime—is specifically designed to be downloaded and run entirely within the user’s browser.8
Its responsibilities are centered on the client-side experience:
- Client-Side Rendering (CSR): This project is the required home for any component intended to run interactively in the browser. If a component is marked with
@rendermode InteractiveWebAssembly
or@rendermode InteractiveAuto
, it must be in this project.8 - Client-Side Logic: Once the WASM runtime is active, this project’s code handles all UI interactions directly in the browser. This provides a fluid, responsive feel without network delays for event handling (
@onclick
), data binding, or running C# logic.11 - Data Fetching via HTTP: The client-side environment is a sandbox; it can’t access server resources directly. Because of this, all data access must be done by making asynchronous HTTP calls to the API endpoints hosted in the Server project (or to external APIs) using an
HttpClient
.14 - Configuration and Services (
Program.cs
): The.Client
project has its own separateProgram.cs
file. This file configures the WebAssembly host and its own DI container. This is where you register client-specific services, most importantly theHttpClient
used for talking to your APIs. It contains the call toAddInteractiveWebAssemblyComponents()
to enable WASM-based rendering.8
The .Client
project contains its own Program.cs
for setting up the WASM host, all the Razor components intended for client-side interactivity, client-side service implementations (like HTTP-based data clients), and a wwwroot
folder for any static assets that are specific to the client application.8
2.3 The Shared Project (Optional but Recommended)
To keep your code clean and avoid duplication, it’s a common and highly recommended practice to add a third project, often called .Shared
. This is a standard.NET class library that is referenced by both the Server and Client projects.
Its purpose is to hold code that needs to be available in both places. This usually includes:
- Data Transfer Objects (DTOs) or view models that define the structure of data passed between the client and server.
- Validation models and their attributes.
- Shared interface definitions, like
IDataService
, which abstract away the details of data access logic.14
You have to be careful with this project to ensure it only contains code and dependencies that are compatible with both the full.NET runtime on the server and the WebAssembly runtime in the browser.
2.4 Table 1: Server Project vs. Client Project: A Comparative Analysis of Responsibilities
The strict separation of duties between these two projects is the bedrock of the Blazor “Auto” model. The following table gives a clear, side-by-side comparison of their distinct roles and capabilities, serving as a vital reference for making architectural decisions.
Feature | Server Project | Client Project |
Execution Environment | On the server (.NET Runtime) | In the browser (WebAssembly Runtime) |
Primary Render Modes | Static SSR, Interactive SSR | Interactive WebAssembly, Interactive Auto |
Data Access Method | Direct access to databases, file systems (e.g., DbContext ) | Networked HTTP calls (HttpClient ) |
API Endpoint Definition | Defines and hosts Minimal APIs / MVC Controllers | Consumes APIs |
Dependency Scope | Can reference any.NET server-side library | Limited to WASM-compatible libraries |
Security Boundary | Trusted environment; can hold secrets (connection strings, API keys) | Untrusted public environment; all code is downloadable and inspectable |
Program.cs Role | Configures ASP.NET Core Host | Configures WebAssembly Host |
Section 3: Component Rendering Lifecycle and Placement Strategy
3.1 The Multi-Stage Lifecycle of an “Auto” Component
To effectively build and debug components, it’s crucial to understand the full journey of a component marked with @rendermode InteractiveAuto
. The process unfolds in several distinct stages across different environments.
Step 1: Prerendering (Server)
By default, any interactive component in a Blazor Web App is first prerendered on the server. The component’s code runs to generate static HTML, which is sent to the browser immediately. The main purpose of this step is to achieve a fast “first paint,” making sure the user sees content as quickly as possible. This improves the perceived performance and is great for Search Engine Optimization (SEO).11 During this server-side run, the component’s
OnInitializedAsync
lifecycle method executes, but OnAfterRenderAsync
does not, because there’s no interactive client environment yet.20
Step 2: Interactive Rendering (Server or Client)
After the static HTML is on the screen, the component is rendered a second time to become “live” and interactive. The environment for this second render depends on what’s in the browser’s cache.
- First Visit (WASM not cached): The framework opens a SignalR connection, and the component becomes interactive using the Blazor Server model. The component’s code runs again on the server, but this time in an interactive context where it can handle user events. The browser’s Document Object Model (DOM) is now managed by the server over that SignalR connection.3
- Subsequent Visits (WASM is cached): If the WebAssembly runtime and application files are already in the browser’s cache, the framework starts up the WASM runtime. The component’s code is executed for a second time, but now it’s running inside the browser. It becomes interactive via Blazor WebAssembly, and no SignalR connection is needed for its UI logic.3
3.2 The “Flicker” Problem: State Loss Between Renders
A major consequence of this multi-stage lifecycle is that the component’s state is not automatically saved between the prerendered (static) version and the later interactive (Server or WASM) version.13 This leads to a common and unpleasant user experience issue often called the UI “flicker.”
Here’s how it usually plays out: during the server-side prerender, the component’s OnInitializedAsync
method fetches data and renders it. The user sees this data instantly. Then, the interactive runtime (either Server or WASM) kicks in, and the component is re-initialized from a blank slate. Its state is empty, so it triggers the data fetch all over again. While this second fetch is happening, the UI might briefly flash back to a “loading…” state before the same data appears once more.12 This extra data fetch is inefficient, and the visible flicker makes the application feel less polished.3
This behavior shows that prerendering is a double-edged sword. While it makes the app feel much faster, it’s also the main source of complexity in the component lifecycle. It forces developers to deal with a dual-rendering model and actively manage how state is preserved.
3.3 The Solution: PersistentComponentState
The official tool provided by the Blazor framework to fix this state loss problem is the PersistentComponentState
service.12 This service acts as a bridge, allowing state that was generated during the server-side prerender to be passed along to the interactive client-side render.
The pattern works like this:
- Persist State on the Server: In your component, inject the
PersistentComponentState
service. During the server-side prerender, after you’ve fetched your data, you register it with the service usingPersistentComponentState.PersistAsJson("key", data)
. This turns the data into JSON and embeds it inside a special marker in the initial HTML that’s sent to the client.12 - Retrieve State on the Client: When the component re-initializes in the interactive context (either Server or WASM), the first thing it should do in its initialization logic is try to get the data back using
PersistentComponentState.TryTakeFromJson("key", out var restoredData)
. - Conditional Data Fetch: If
TryTakeFromJson
succeeds, the component uses the restored data and, most importantly, skips the redundant database or network call. If it fails (which might happen on a purely client-side navigation where no prerendering took place), the component just proceeds with its normal data fetching logic.12
By using this pattern, PersistentComponentState
becomes more than just an optimization; it’s an essential tool for building professional-grade user interfaces that feel smooth and performant.
3.4 The Golden Rule of Component Placement
The architectural rules of the “Auto” model lead to one clear, non-negotiable rule for organizing your components:
Any component that needs to run interactively in the browser using @rendermode InteractiveAuto
or @rendermode InteractiveWebAssembly
MUST be placed in the .Client
project.8
The reasoning for this rule is absolute and comes directly from the architecture:
- The
.Client
project is the only project whose compiled output is bundled and sent to the browser. A component that physically exists in the Server project’s file system will never be downloaded and therefore can’t be run by the WebAssembly runtime.10 - This rule applies to the entire component tree. A component marked for client-side rendering can’t have a child component that only exists in the Server project. Everything from the parent down must be available to the client.7
- This has a major ripple effect on your application’s design. For example, if your app needs an interactive navigation menu or other shared UI in its main layout, that
MainLayout.razor
component must be marked as interactive and, therefore, must live in the.Client
project.21 Since the layout usually wraps every page, this decision effectively shifts the application’s architectural center of gravity toward the client. It establishes a “client-first” default for how you design components, reinforcing the need for all shared UI to be WASM-compatible and solidifying the boundary between the two projects.17
On the flip side, components that are meant to be purely static or run only with @rendermode InteractiveServer
can and should stay in the Server project, as there’s no reason for them to be downloaded to the client.9
Section 4: Data Access and API Architecture in a Hybrid Environment
4.1 The Data Access Dichotomy
The fact that “Auto” mode components can run in two different places creates a big challenge for data access. A component in the .Client
project has to be able to fetch data whether it’s currently running on the server (during prerendering or as a Blazor Server component) or in the browser (via WebAssembly).15
- In the Server Context: When the component’s code is running on the server, it’s in a trusted environment with direct access to backend resources. For the best performance and simplicity, it should talk to the database directly, for instance, by using an injected Entity Framework Core
DbContext
.18 - In the Client Context: When the exact same component’s code is running in the browser, it’s in a sandboxed, untrusted environment. It has no direct line to the database. All data access must be done by making network calls over HTTP to well-defined API endpoints.14
You can’t just sprinkle if (runningOnServer)
checks throughout your component’s logic; that would be messy, hard to maintain, and would violate core design principles.
4.2 Architectural Pattern: The Abstracted Data Service
The definitive architectural pattern to solve this problem is to hide all data operations behind a service interface.10 This approach decouples the component from the nitty-gritty details of data access, making it unaware of its own execution environment.
The pattern has three key parts:
- The Interface (in the
.Shared
project): Define a contract for your data operations. For example, anIUserService
might define a methodTask<User> GetUsersAsync();
. Putting this in the.Shared
project makes it available to both the Server and Client projects. - The Server Implementation (in the
.Server
project): Create a class,ServerUserService
, that implementsIUserService
. This class will inject theDbContext
and implementGetUsersAsync
by running a direct database query. You then register this implementation with the DI container in the server’sProgram.cs
file. - The Client Implementation (in the
.Client
project): Create another class,ClientUserService
, that also implementsIUserService
. This class will injectHttpClient
and implementGetUsersAsync
by making an HTTP GET request to your/api/users
endpoint. You register this implementation with the DI container in the client’sProgram.cs
file.
Now, inside your Razor component (which lives in the .Client
project), the code becomes clean and simple: @inject IUserService UserService
. When the component runs, the correct DI container (server-side or client-side) automatically provides the right implementation. This pattern is essential for writing maintainable and robust components in an “Auto” mode application.15
4.3 API Endpoint Design and Consumption
API endpoints act as the public contract between your server and client. They should be defined exclusively in the Server project. Minimal APIs, introduced in.NET 6, are a lean and highly effective way to create these endpoints.13
A critical and often overlooked detail in this architecture is how HttpClient
is configured. Because your client-side services can actually be executed on the server during prerendering, HttpClient
needs to be configured in both projects:
- In the Client project’s
Program.cs
, you must register anHttpClient
instance with the base address of your server application. This is the client that will be used for all API calls made from the browser via WebAssembly.13 - In the Server project’s
Program.cs
, you also need a call tobuilder.Services.AddHttpClient();
.13 This might seem strange, but it’s absolutely necessary. During prerendering, the server’s DI container has to create instances of all the services your component needs, including the client-side data service. Since that service depends onHttpClient
, the server’s DI container must know how to provide one. If you forget this registration, your application will crash with a service resolution error on the initial page load.13 This detail underscores the fact that during prerendering, the server is effectively simulating the client’s dependency tree.
4.4 Advanced Topic: Authentication and the Backend-for-Frontend (BFF) Pattern
Authentication in a hybrid execution model like this is naturally complex. While standard cookie-based authentication works flawlessly during server-side execution, managing and sending credentials like JWT bearer tokens from a WebAssembly client requires careful security planning.20
For “Auto” mode applications, the ASP.NET Core team officially recommends the Backend-for-Frontend (BFF) pattern as the most secure and reliable approach.16 This pattern elegantly solves the identity and trust issues between the server and client environments.
Here’s how the BFF pattern works:
- The user authenticates directly with the Server project. The server establishes a secure, server-side session, usually managed by an encrypted cookie. The server is responsible for securely getting and storing any access or refresh tokens from an identity provider.
- When the WebAssembly client needs to call a protected API, it doesn’t try to handle tokens or call the external API itself. Instead, it makes a simple, authenticated request (using its browser cookie) to a dedicated API endpoint on its own Server project.
- This endpoint on the Server project acts as a secure proxy. It receives the request, confirms the user’s identity via the session cookie, attaches the securely stored JWT access token to the request, and then forwards the request to the actual protected API downstream.
This pattern offers a huge security advantage by making sure that sensitive tokens are never exposed to the browser, which helps protect against risks like cross-site scripting (XSS) attacks.16 It centralizes all the complex authentication logic on the trusted server, allowing the WebAssembly client to stay simple and work within the browser’s standard, secure cookie-based model. The Server project is thus transformed into a trusted security gateway for the Client project, further cementing their architectural relationship.
Conclusion and Strategic Recommendations
Blazor’s “Auto” render mode is a sophisticated step forward in web UI development, offering a powerful way to resolve the long-standing conflict between initial load time and client-side interactivity. However, this power comes with a higher degree of architectural complexity. To succeed with the “Auto” model, developers need to shift from a monolithic mindset and embrace the principles of a distributed system, where code must be designed to function across distinct and physically separate execution environments.
Our analysis of its architecture has highlighted several core truths: “Auto” mode is fundamentally a strategy for optimizing Time-to-Interactivity; the dual-project structure is a necessary boundary that enforces a separation of concerns; the multi-stage component lifecycle requires deliberate state management; and the data access dichotomy demands careful abstraction.
Actionable Recommendations for Developers
Based on this architectural breakdown, here are several strategic recommendations for developers and teams who are adopting the Blazor “Auto” render mode:
- Embrace the Architecture: Don’t try to fight or work around the dual-project structure. Instead, understand and respect the separation of concerns it enforces. This boundary is a feature, not a bug, that guides you toward a more secure and maintainable design.
- Client-First for UI: As a general rule, place all components that might become interactive, including shared layouts and navigation, in the
.Client
project from the very beginning. This will save you from painful refactoring down the road and aligns your project structure with how the application’s UI is likely to evolve. - Abstract All Data Access: Never write
DbContext
orHttpClient
logic directly inside a Razor component. Always funnel data access through an abstracted service interface that you define in a.Shared
project. This is the single most important pattern for creating components that can work anywhere. - Manage State Explicitly: Make it a habit to use
PersistentComponentState
for any interactive component that fetches data when it initializes. This should be a standard part of your development process, not an afterthought, to prevent UI flicker and get rid of redundant API calls. - Configure DI in Both Projects: Remember to configure necessary services, especially
HttpClient
, in both the server and clientProgram.cs
files. This is a critical step to ensure your components can be prerendered on the server without causing runtime dependency injection errors. - Prioritize the BFF Pattern for Authentication: For any application with serious authentication or authorization needs, adopt the Backend-for-Frontend pattern. This is the recommended way to ensure robust security by centralizing token management on the trusted server and keeping sensitive credentials out of the browser.
Ultimately, the Blazor “Auto” render mode is more than just a new feature; it’s a paradigm shift. It requires a corresponding shift in architectural thinking. By understanding its core principles and consistently applying the patterns discussed in this report, developers can unlock its full potential to build the next generation of fast, powerful, and scalable web applications with.NET.