Passing Data from the Controller to the View
| Product family | ASP.NET Core |
|---|---|
| Document source | Aspnet Core Aspnetcore 10.0 |
| Guide type | Reference Guide |
| Skill level | Intermediate to advanced |
| Time | 15 - 60 minutes depending on environment |
This page documents Passing Data from the Controller to the View for engineers working with ASP.NET Core. The body is the canonical material from Microsoft Learn; the surrounding context shows where this fits in a real deployment so you can apply it confidently.
Hands-on walkthrough
MVC has been around in ASP.NET land since 2009. The patterns are mature, the conventions are well-trodden, and there's still a way to mess it up. Passing Data from the Controller to the View is one of those topics where you read the docs, nod, and then trip on the first edge case in production. Here's how I work through it.
I once watched a senior dev waste an entire Friday afternoon on this. The cause? A trailing slash in an OIDC redirect URI. I'm not joking.
What you should have ready
- .NET 10 SDK installed.
- An ASP.NET Core MVC project. Create one with
dotnet new mvc -n MyMvcAppif you're starting fresh. Takes 6 seconds, ships about 80 files. - Either Visual Studio 2026 or VS Code with the C# Dev Kit.
- A browser you can spin up quickly with DevTools. I keep Brave for this; it doesn't autofill noise into form fields.
Walkthrough
- Create or open the controller. Right-click on the Controllers folder > Add > Controller. Or do it from the CLI by hand: a controller is just a class that inherits from
Controllerand has action methods. - Decide on action methods. Standard CRUD looks like Index (list), Details (single), Create (form), Edit (update form), Delete (remove). Stick with the convention. The MVC tooling will thank you.
- Wire the controller into routing. Default convention in
Program.csisapp.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"). If your controller doesn't show up at the URL you expect, the route map is the first thing to check. - Create the views. Per controller action, a Razor view in Views/<Controller>/<Action>.cshtml. Use the View > Add Scaffolding for forms; it generates correct anti-forgery tokens automatically.
- Pass data via the model. Strongly-typed views with
@model MyViewModelat the top. Don't useViewBagbeyond simple one-offs; it makes refactoring painful. - Add validation. Data annotations on the view model:
[Required],[StringLength(100)],[EmailAddress]. Client-side validation kicks in automatically with the jQuery validation scripts. - Run with
dotnet watch run. Hot reload picks up controller and view changes in 1-2 seconds.
How I verify MVC plumbing
- Hit the URL with a browser. 200 means routing worked. 404 usually means controller or action name mismatch.
- Open DevTools, Network, and check the response Content-Type. text/html means you're hitting the view. application/json means you forgot you're using API controllers.
- Submit a form and check the POST landed on the right action. Use a breakpoint or a
_logger.LogInformationat the top of the action. - Run
dotnet testif you have controller-level tests.WebApplicationFactoryis the standard pattern.
Things that go wrong
- Anti-forgery token mismatch. If you removed the
@Html.AntiForgeryToken()from a form or skipped[ValidateAntiForgeryToken]on the action, you'll see a 400 with no obvious error. Look at the Response body, the framework writes a clear message. - Action name vs view file name mismatch. Razor view discovery is convention-based. Action
EditDetailslooks for Views/Foo/EditDetails.cshtml. Get the casing right. - Tag helpers not active. Need
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpersin _ViewImports.cshtml. Without it, asp-for and asp-action just render as inert attributes. - Model binder silently dropping properties. If your view model has a property the form didn't post, it stays as default. If the form posts a property your model doesn't have, it's silently discarded. Test with the full payload.
Rollback
MVC changes are usually localized to a few files. Git revert is enough. If the change touched routing in Program.cs, double-check no other controller's URL changed as a side effect.
If you hit a snag, drop the exact error from the trace log into a search engine before assuming Microsoft broke something — nine times out of ten, it's a config drift you can fix in under 15 minutes.
Operational notes
Let me extend this with the parts the docs don't always say out loud.
Team and runbook. Pair this with code review checklist items. A two-line note in the PR template covers 80% of the recurring miss.
Cost watch. Free-tier limits move. What costs zero today may cost $0.50 per million requests next year. Set a budget alert at 50% of expected monthly spend — costs you nothing, saves you from a surprise bill.
Security pass. Run a quick OWASP check. Inputs, outputs, authentication, authorization, logging. Five minutes of scanning catches the obvious mistakes.
Observability. Add a span. Distributed tracing is cheap to add and expensive to retrofit. System.Diagnostics.Activity is the API; ActivitySource + ActivityListener is the wiring.
Patterns I keep coming back to
Across many projects in ASP.NET Core, a few patterns repeat that are worth naming explicitly.
- The "smoke test on every deploy" pattern. A 10-line script that hits 5 endpoints, checks response code and latency, exits non-zero on failure. Run it as the last step of every CD pipeline. Catches 70% of post-deploy regressions before they reach a user.
- The "feature flag everything risky" pattern. Microsoft.FeatureManagement.AspNetCore (free, ships with the framework). Wrap any change that could blow up behind a flag, default off in production, flip the flag after the change is verified.
- The "twin environment" pattern. A staging environment that is byte-for-byte identical to production in configuration, with a fraction of the data. Costs maybe 20% extra on Azure if you size sensibly. Catches roughly 50% of "works locally, fails in prod" bugs.
- The "single source of secrets" pattern. Azure Key Vault or HashiCorp Vault. Not a .env file. Not appsettings.Production.json with values committed. Real secret storage with rotation policies.
Decisions worth revisiting periodically
Some choices made early in a project age fast. I keep a list of "look at this every six months" items per project. ASP.NET Core-flavored versions look like this:
- SDK and framework version. Are you on the current LTS? Microsoft's support window is fixed; falling behind two majors means you're paying interest in security patches.
- Dependency surface. Run
dotnet list package --outdatedquarterly. Each outdated package is a small risk; cumulatively, large risk. - Build time. If your CI builds creep past 8-10 minutes, the developer feedback loop slows enough to hurt productivity. Profile and trim.
- Test coverage and execution time. Coverage that climbs slowly is fine. Test suites that climb slowly toward 30 minutes is a warning sign: refactor or parallelize.
- Production error budget. If you're hitting your error budget consistently, slow feature work and pay down operational debt. If you're under-using your error budget, you're shipping too slowly.
Why I do it this way
Years ago I treated each topic in isolation. I'd read the docs, implement the feature, ship it, move on. The result was a lot of features that worked individually and didn't compose well. Today I default to: read the docs, sketch a diagram on paper or a digital whiteboard, identify the trust boundary, identify the failure mode, then implement. Ten extra minutes up front, hours saved down the road.
The other shift was treating tests as the design tool, not the verification tool. Writing the test first forces you to think about the API surface from the caller's perspective. By the time the test compiles, the design has been pressure-tested by your own most demanding consumer: a test that has to drive the feature without knowing the implementation.
Practical example: last month I rewrote a small reporting endpoint. The first draft was a single 60-line action method that pulled data, transformed it, and returned JSON. Tests forced me to split it into an IReportRepository, an IReportFormatter, and a thin controller. Same functionality, but now I can swap the formatter for HTML output by writing one new class and zero changes to the controller. That's the power of writing the test first.
References worth bookmarking
- Microsoft Learn, the canonical source. Whatever version of this page exists at learn.microsoft.com beats my summary.
- The ASP.NET Core GitHub repo's issues and discussions. Real bugs and real solutions, sometimes faster than the docs catch up.
- The .NET API browser at learn.microsoft.com/en-us/dotnet/api. perfect for "what does this method actually return when X."
- The official .NET blog at devblogs.microsoft.com/dotnet, explains the "why" behind changes that the API docs leave implicit.
How to apply this in practice
- Treat this as a starting point. Your tenant, SKU, region, and licence level all change the surface area in small but real ways.
- Run the procedure in a non-production environment first. A staging slot, a dev tenant, a sandboxed subscription: pick one and use it.
- Pin the version you implement against. When you commit to a design choice based on this page, write the date and the exact ASP.NET Core version into your ADR (architecture decision record).
- Cross-check the current Microsoft Learn page before rolling production. The product team updates docs often; what's true today may shift in two months.
Caveats and what to double-check
- ASP.NET Core terminology drifts. The same concept can have two or three names across docs cohorts written in different years.
- Some features in this area may still be in preview, confirm GA status before relying on it for production SLAs.
- Regional availability varies. A feature documented as "global" may actually roll out region by region across months.
- Pricing for ASP.NET Core-related services changes regularly. This page does not track pricing. Use the official Microsoft pricing calculator for the latest numbers.
Related work in your environment
- Document this reference in your team wiki along with which workloads currently depend on it.
- Set up a Microsoft Learn RSS feed or doc-change alert for the source page so your team is notified when Microsoft updates the canonical version.
- Add a periodic review to your governance cadence. Quarterly is a sensible default for ASP.NET Core.
FAQ
References
- Microsoft Learn - official documentation for ASP.NET Core
- Microsoft tech community forums and Q&A
- Azure / Microsoft 365 service health dashboards
Related fixes
Related guides worth a look while you sort this one out: