18 KiB
title | author | description | ms.author | ms.custom | ms.date | uid |
---|---|---|---|---|---|---|
Razor Pages with Entity Framework Core in ASP.NET Core - Tutorial 1 of 8 | rick-anderson | Shows how to create a Razor Pages app using Entity Framework Core | riande | seodec18 | 11/22/2018 | data/ef-rp/intro |
Razor Pages with Entity Framework Core in ASP.NET Core - Tutorial 1 of 8
[!INCLUDE2.0 version]
::: moniker range=">= aspnetcore-2.1"
By Tom Dykstra and Rick Anderson
The Contoso University sample web app demonstrates how to create an ASP.NET Core Razor Pages app using Entity Framework (EF) Core.
The sample app is a web site for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This page is the first in a series of tutorials that explain how to build the Contoso University sample app.
Download or view the completed app. Download instructions.
Prerequisites
Visual Studio
.NET Core CLI
Familiarity with Razor Pages. New programmers should complete Get started with Razor Pages before starting this series.
Troubleshooting
If you run into a problem you can't resolve, you can generally find the solution by comparing your code to the completed project. A good way to get help is by posting a question to StackOverflow.com for ASP.NET Core or EF Core.
The Contoso University web app
The app built in these tutorials is a basic university web site.
Users can view and update student, course, and instructor information. Here are a few of the screens created in the tutorial.
The UI style of this site is close to what's generated by the built-in templates. The tutorial focus is on EF Core with Razor Pages, not the UI.
Create the ContosoUniversity Razor Pages web app
Visual Studio
- From the Visual Studio File menu, select New > Project.
- Create a new ASP.NET Core Web Application. Name the project ContosoUniversity. It's important to name the project ContosoUniversity so the namespaces match when code is copy/pasted.
- Select ASP.NET Core 2.1 in the dropdown, and then select Web Application.
For images of the preceding steps, see Create a Razor web app. Run the app.
.NET Core CLI
dotnet new webapp -o ContosoUniversity
cd ContosoUniversity
dotnet run
Set up the site style
A few changes set up the site menu, layout, and home page. Update Pages/Shared/_Layout.cshtml with the following changes:
-
Change each occurrence of "ContosoUniversity" to "Contoso University". There are three occurrences.
-
Add menu entries for Students, Courses, Instructors, and Departments, and delete the Contact menu entry.
The changes are highlighted. (All the markup is not displayed.)
In Pages/Index.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET and MVC with text about this app:
Create the data model
Create entity classes for the Contoso University app. Start with the following three entities:
There's a one-to-many relationship between Student
and Enrollment
entities. There's a one-to-many relationship between Course
and Enrollment
entities. A student can enroll in any number of courses. A course can have any number of students enrolled in it.
In the following sections, a class for each one of these entities is created.
The Student entity
Create a Models folder. In the Models folder, create a class file named Student.cs with the following code:
The ID
property becomes the primary key column of the database (DB) table that corresponds to this class. By default, EF Core interprets a property that's named ID
or classnameID
as the primary key. In classnameID
, classname
is the name of the class. The alternative automatically recognized primary key is StudentID
in the preceding example.
The Enrollments
property is a navigation property. Navigation properties link to other entities that are related to this entity. In this case, the Enrollments
property of a Student entity
holds all of the Enrollment
entities that are related to that Student
. For example, if a Student row in the DB has two related Enrollment rows, the Enrollments
navigation property contains those two Enrollment
entities. A related Enrollment
row is a row that contains that student's primary key value in the StudentID
column. For example, suppose the student with ID=1 has two rows in the Enrollment
table. The Enrollment
table has two rows with StudentID
= 1. StudentID
is a foreign key in the Enrollment
table that specifies the student in the Student
table.
If a navigation property can hold multiple entities, the navigation property must be a list type, such as ICollection<T>
. ICollection<T>
can be specified, or a type such as List<T>
or HashSet<T>
. When ICollection<T>
is used, EF Core creates a HashSet<T>
collection by default. Navigation properties that hold multiple entities come from many-to-many and one-to-many relationships.
The Enrollment entity
In the Models folder, create Enrollment.cs with the following code:
The EnrollmentID
property is the primary key. This entity uses the classnameID
pattern instead of ID
like the Student
entity. Typically developers choose one pattern and use it throughout the data model. In a later tutorial, using ID without classname is shown to make it easier to implement inheritance in the data model.
The Grade
property is an enum
. The question mark after the Grade
type declaration indicates that the Grade
property is nullable. A grade that's null is different from a zero grade -- null means a grade isn't known or hasn't been assigned yet.
The StudentID
property is a foreign key, and the corresponding navigation property is Student
. An Enrollment
entity is associated with one Student
entity, so the property contains a single Student
entity. The Student
entity differs from the Student.Enrollments
navigation property, which contains multiple Enrollment
entities.
The CourseID
property is a foreign key, and the corresponding navigation property is Course
. An Enrollment
entity is associated with one Course
entity.
EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>
. For example,StudentID
for the Student
navigation property, since the Student
entity's primary key is ID
. Foreign key properties can also be named <primary key property name>
. For example, CourseID
since the Course
entity's primary key is CourseID
.
The Course entity
In the Models folder, create Course.cs with the following code:
The Enrollments
property is a navigation property. A Course
entity can be related to any number of Enrollment
entities.
The DatabaseGenerated
attribute allows the app to specify the primary key rather than having the DB generate it.
Scaffold the student model
In this section, the student model is scaffolded. That is, the scaffolding tool produces pages for Create, Read, Update, and Delete (CRUD) operations for the student model.
- Build the project.
- Create the Pages/Students folder.
Visual Studio
- In Solution Explorer, right click on the Pages/Students folder > Add > New Scaffolded Item.
- In the Add Scaffold dialog, select Razor Pages using Entity Framework (CRUD) > ADD.
Complete the Add Razor Pages using Entity Framework (CRUD) dialog:
- In the Model class drop-down, select Student (ContosoUniversity.Models).
- In the Data context class row, select the + (plus) sign and change the generated name to ContosoUniversity.Models.SchoolContext.
- In the Data context class drop-down, select ContosoUniversity.Models.SchoolContext
- Select Add.
See Scaffold the movie model if you have a problem with the preceding step.
.NET Core CLI
Run the following commands to scaffold the student model.
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design --version 2.1.0
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Models.SchoolContext -udl -outDir Pages\Students --referenceScriptLibraries
The scaffold process created and changed the following files:
Files created
- Pages/Students Create, Delete, Details, Edit, Index.
- Data/SchoolContext.cs
File updates
- Startup.cs : Changes to this file are detailed in the next section.
- appsettings.json : The connection string used to connect to a local database is added.
Examine the context registered with dependency injection
ASP.NET Core is built with dependency injection. Services (such as the EF Core DB context) are registered with dependency injection during application startup. Components that require these services (such as Razor Pages) are provided these services via constructor parameters. The constructor code that gets a db context instance is shown later in the tutorial.
The scaffolding tool automatically created a DB Context and registered it with the dependency injection container.
Examine the ConfigureServices
method in Startup.cs. The highlighted line was added by the scaffolder:
The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.
Update main
In Program.cs, modify the Main
method to do the following:
- Get a DB context instance from the dependency injection container.
- Call the EnsureCreated.
- Dispose the context when the
EnsureCreated
method completes.
The following code shows the updated Program.cs file.
EnsureCreated
ensures that the database for the context exists. If it exists, no action is taken. If it does not exist, then the database and all its schema are created. EnsureCreated
does not use migrations to create the database. A database that is created with EnsureCreated
cannot be later updated using migrations.
EnsureCreated
is called on app start, which allows the following work flow:
- Delete the DB.
- Change the DB schema (for example, add an
EmailAddress
field). - Run the app.
EnsureCreated
creates a DB with theEmailAddress
column.
EnsureCreated
is convenient early in development when the schema is rapidly evolving. Later in the tutorial the DB is deleted and migrations are used.
Test the app
Run the app and accept the cookie policy. This app doesn't keep personal information. You can read about the cookie policy at EU General Data Protection Regulation (GDPR) support.
- Select the Students link and then Create New.
- Test the Edit, Details, and Delete links.
Examine the SchoolContext DB context
The main class that coordinates EF Core functionality for a given data model is the DB context class. The data context is derived from Microsoft.EntityFrameworkCore.DbContext. The data context specifies which entities are included in the data model. In this project, the class is named SchoolContext
.
Update SchoolContext.cs with the following code:
The highlighted code creates a DbSet<TEntity> property for each entity set. In EF Core terminology:
- An entity set typically corresponds to a DB table.
- An entity corresponds to a row in the table.
DbSet<Enrollment>
and DbSet<Course>
could be omitted. EF Core includes them implicitly because the Student
entity references the Enrollment
entity, and the Enrollment
entity references the Course
entity. For this tutorial, keep DbSet<Enrollment>
and DbSet<Course>
in the SchoolContext
.
SQL Server Express LocalDB
The connection string specifies SQL Server LocalDB. LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. LocalDB starts on demand and runs in user mode, so there's no complex configuration. By default, LocalDB creates .mdf DB files in the C:/Users/<user>
directory.
Add code to initialize the DB with test data
EF Core creates an empty DB. In this section, an Initialize
method is written to populate it with test data.
In the Data folder, create a new class file named DbInitializer.cs and add the following code:
The code checks if there are any students in the DB. If there are no students in the DB, the DB is initialized with test data. It loads test data into arrays rather than List<T>
collections to optimize performance.
The EnsureCreated
method automatically creates the DB for the DB context. If the DB exists, EnsureCreated
returns without modifying the DB.
In Program.cs, modify the Main
method to call Initialize
:
Delete any student records and restart the app. If the DB is not initialized, set a break point in Initialize
to diagnose the problem.
View the DB
Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio. In SSOX, click (localdb)\MSSQLLocalDB > Databases > ContosoUniversity1.
Expand the Tables node.
Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.
Asynchronous code
Asynchronous programming is the default mode for ASP.NET Core and EF Core.
A web server has a limited number of threads available, and in high load situations all of the available threads might be in use. When that happens, the server can't process new requests until the threads are freed up. With synchronous code, many threads may be tied up while they aren't actually doing any work because they're waiting for I/O to complete. With asynchronous code, when a process is waiting for I/O to complete, its thread is freed up for the server to use for processing other requests. As a result, asynchronous code enables server resources to be used more efficiently, and the server is enabled to handle more traffic without delays.
Asynchronous code does introduce a small amount of overhead at run time. For low traffic situations, the performance hit is negligible, while for high traffic situations, the potential performance improvement is substantial.
In the following code, the async keyword, Task<T>
return value, await
keyword, and ToListAsync
method make the code execute asynchronously.
-
The
async
keyword tells the compiler to:- Generate callbacks for parts of the method body.
- Automatically create the Task object that's returned. For more information, see Task Return Type.
-
The implicit return type
Task
represents ongoing work. -
The
await
keyword causes the compiler to split the method into two parts. The first part ends with the operation that's started asynchronously. The second part is put into a callback method that's called when the operation completes. -
ToListAsync
is the asynchronous version of theToList
extension method.
Some things to be aware of when writing asynchronous code that uses EF Core:
- Only statements that cause queries or commands to be sent to the DB are executed asynchronously. That includes,
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
, andSaveChangesAsync
. It doesn't include statements that just change anIQueryable
, such asvar students = context.Students.Where(s => s.LastName == "Davolio")
. - An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
- To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the DB.
For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.
In the next tutorial, basic CRUD (create, read, update, delete) operations are examined.
::: moniker-end
[!div class="step-by-step"] Next