To create a guestbook in modern ASP.NET, build an ASP.NET Core Razor Pages app, define a guestbook-entry model, save submissions with Entity Framework Core (EF Core), and render a limited list of saved entries. This guide uses a simple design with a visitor name, message, and server-generated timestamp. It targets ASP.NET Core Razor Pages—not classic ASP.NET Web Forms or older ASP.NET MVC, whose project templates and implementation steps differ.
Why use Razor Pages for a guestbook?
A guestbook is a page-oriented feature: visitors submit a form, the app validates and stores the entry, and a page displays saved entries. Razor Pages is a natural option for that flow. Microsoft describes it this way: “Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app.” Its page classes support model binding, and Razor Tag Helpers connect form fields to page properties.
Microsoft’s Razor Pages tutorials demonstrate these building blocks with a movie database application, not a guestbook. The model and page behavior below are a guestbook adaptation. The current Microsoft Learn tutorials cover app creation, models, databases, and validation; the EF Core walkthrough demonstrates a code-first approach and database-context registration. See Razor Pages architecture and concepts, the Razor Pages tutorial series, and Razor Pages with Entity Framework Core.
Create the Razor Pages project
Use the ASP.NET Core SDK and a Razor Pages project template. Microsoft’s current tutorial starts its sample with dotnet new webapp; the resulting project includes Razor Pages support. Confirm that the installed SDK and any EF Core packages match the target .NET version before publishing or deploying, since SDK releases and support periods change.
#1 Best Overall
-
In a terminal, create and enter a project directory:
dotnet new webapp -n Guestbookcd Guestbook -
Run the starter app to confirm the SDK and template are working:
dotnet run -
Open the project in your editor. The default template places Razor Pages under
Pages; add the guestbook page and supporting model and data files there or in folders appropriate to your project.
Choose a database provider
EF Core connects the guestbook model to a database through a DbContext registered with the app’s dependency-injection container. Microsoft’s tutorial demonstrates SQLite in its Visual Studio Code path and SQL Server LocalDB in its Visual Studio path, and points readers to production-provider guidance. For a small local learning project, SQLite keeps setup straightforward; choose a provider that also fits the app’s hosting environment and operational needs.
Rank #2
| Choice | What the tutorial establishes | Considerations |
|---|---|---|
| SQLite | Used in Microsoft’s Visual Studio Code walkthrough. | Convenient for a local learning project. SQLite has migration limitations for some schema changes, including changing or removing a column. |
| SQL Server LocalDB | Used in Microsoft’s Visual Studio walkthrough. | Provides a SQL Server development path. Select and configure a production database separately for the target environment. |
For a current walkthrough of the database setup and its provider paths, see Microsoft’s EF Core Razor Pages tutorial. If a SQLite schema change cannot be handled by the available migration operation, Microsoft’s database tutorial describes dropping and recreating the database for its tutorial scenario. That is not a safe general strategy for a deployed guestbook with real submissions: use a migration and data-preservation plan appropriate to the database and production data.
Define the guestbook entry
A minimal entry needs a key, display name, message, and creation time. These are practical design choices for this example, not a schema prescribed by Microsoft. Require the visitor-controlled fields and set a maximum message length that suits the site.
using System.ComponentModel.DataAnnotations;
namespace Guestbook.Models;
public class GuestbookEntry
{
public int Id { get; set; }
[Required]
[StringLength(80)]
public string DisplayName { get; set; } = string.Empty;
[Required]
[StringLength(2000)]
public string Message { get; set; } = string.Empty;
public DateTime CreatedAtUtc { get; set; }
}
The limits above are example application rules. Adjust them for the guestbook’s intended use, and keep the timestamp under server control rather than accepting it from the visitor.
Connect EF Core to the app
Create a context that exposes the entries as a database set:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →using Guestbook.Models;
using Microsoft.EntityFrameworkCore;
namespace Guestbook.Data;
public class GuestbookContext(DbContextOptions<GuestbookContext> options)
: DbContext(options)
{
public DbSet<GuestbookEntry> Entries => Set<GuestbookEntry>();
}
Register the context in Program.cs with the provider selected for the project. For example, a SQLite app’s registration has this shape:
using Guestbook.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<GuestbookContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("Guestbook")));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Install the EF Core provider package compatible with the project’s EF Core version, and add a connection string named Guestbook in configuration. For SQLite, that string identifies the database file; for SQL Server, configure the corresponding SQL Server provider and connection string instead. Avoid putting production credentials in source-controlled settings.
After defining the model and context, create the database using the EF Core migration workflow for the installed tooling and provider. Microsoft’s tutorial walks through its code-first setup and database creation; follow its steps for the target .NET and EF Core versions rather than mixing commands or package versions from different releases.
Build the submission page
Create a Razor Page with a bound input model and a POST handler. Bind only the visitor-editable fields. The page creates the entity, assigns its timestamp on the server, saves valid input asynchronously, and redirects after success.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteIn Pages/Index.cshtml.cs:
using Guestbook.Data;
using Guestbook.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
namespace Guestbook.Pages;
public class IndexModel(GuestbookContext db) : PageModel
{
private const int PageSize = 20;
[BindProperty]
public EntryInput Input { get; set; } = new();
public IReadOnlyList<GuestbookEntry> Entries { get; private set; } = [];
public async Task OnGetAsync()
{
Entries = await db.Entries
.OrderByDescending(entry => entry.CreatedAtUtc)
.ThenByDescending(entry => entry.Id)
.Take(PageSize)
.ToListAsync();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
await LoadEntriesAsync();
return Page();
}
var entry = new GuestbookEntry
{
DisplayName = Input.DisplayName.Trim(),
Message = Input.Message.Trim(),
CreatedAtUtc = DateTime.UtcNow
};
db.Entries.Add(entry);
await db.SaveChangesAsync();
return RedirectToPage();
}
private async Task LoadEntriesAsync()
{
Entries = await db.Entries
.OrderByDescending(entry => entry.CreatedAtUtc)
.ThenByDescending(entry => entry.Id)
.Take(PageSize)
.ToListAsync();
}
public class EntryInput
{
[System.ComponentModel.DataAnnotations.Required]
[System.ComponentModel.DataAnnotations.StringLength(80)]
public string DisplayName { get; set; } = string.Empty;
[System.ComponentModel.DataAnnotations.Required]
[System.ComponentModel.DataAnnotations.StringLength(2000)]
public string Message { get; set; } = string.Empty;
}
}
The input model deliberately contains no identifier or timestamp, so those values are not accepted from the form. If validation fails, the handler reloads the entries and returns the page with model-state errors instead of saving. The post/redirect/get flow prevents a normal browser refresh from immediately resubmitting the successful form.
In Pages/Index.cshtml, add the form and render the stored entries:
@page
@model Guestbook.Pages.IndexModel
<h1>Guestbook</h1>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Input.DisplayName"></label>
<input asp-for="Input.DisplayName" />
<span asp-validation-for="Input.DisplayName"></span>
</div>
<div>
<label asp-for="Input.Message"></label>
<textarea asp-for="Input.Message" rows="5"></textarea>
<span asp-validation-for="Input.Message"></span>
</div>
<button type="submit">Sign the guestbook</button>
</form>
<h2>Recent entries</h2>
@if (Model.Entries.Count == 0)
{
<p>No entries yet. Be the first to sign the guestbook.</p>
}
else
{
foreach (var entry in Model.Entries)
{
<article>
<h3>@entry.DisplayName</h3>
<p>@entry.Message</p>
<time datetime="@entry.CreatedAtUtc.ToString("O")">
@entry.CreatedAtUtc.ToString("u") UTC
</time>
</article>
}
}
Ensure the project’s shared layout includes the validation scripts if you want client-side validation as well. Client-side checks improve the form experience, but the page handler must still validate on the server before saving.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Keep the entry list bounded
The example orders newest entries first and uses a secondary key to keep ordering stable when timestamps match. It retrieves at most 20 rows per request rather than loading an unbounded table. Microsoft’s EF Core Razor Pages tutorial cautions against retrieving an arbitrary number of rows and demonstrates limiting a query with Take; see its sorting, filtering, and paging tutorial. For a growing guestbook, add paging or another deliberate browsing strategy instead of increasing the limit without bounds.
Recommended Free Tools
Best Value
- Applying all key ASP.NET Core components, including MVC for HTML generation, .NET Core, EF Core, ASP.NET Identity, dependency injection, and more
- Integrating ASP.NET Core with leading client-side frameworks, including Bootstrap
- ASP.NET Core code for implementing business logic and data transformations
- Handling configuration, routing, controllers, views, and common tasks (including posting forms and presenting data)
- Performing complementary tasks: error handling, logging, application design, authentication, localization, and more
Before making the guestbook public
Required fields and length limits help define acceptable input; they do not, by themselves, make a public submission feature safe or manageable. Decide how entries will be reviewed and how the site will handle unwanted submissions before opening the form to everyone.
-
Review output rendering and preserve framework protections; do not render visitor text as trusted HTML.
-
Choose whether entries appear immediately or wait for moderation, and establish how a visitor or administrator can report or remove an entry.
-
Evaluate appropriate anti-spam measures for the audience and deployment rather than assuming form validation will prevent abuse.
Free tools Windows power users keep installed
One-click scans. No signup required.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy. -
Set privacy and retention practices that fit what the site collects and where it operates.
-
Plan database schema changes so a deployment does not discard real visitor submissions.
Where to continue
Microsoft’s Razor Pages tutorial series provides a structured path through app setup, models, database work, and validation, while its EF Core Razor Pages tutorial shows the database-backed pattern. The examples there use a movie app; apply the same concepts to the guestbook’s entry model and page flow, checking the documentation for the version you deploy.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

