aspnetcore/data/ef-rp/intro.md
By Tom Dykstra, Jeremy Likness, and Jon P Smith
:::moniker range=">= aspnetcore-6.0"
This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. The tutorials build a web site for a fictional Contoso University. The site includes functionality such as student admission, course creation, and instructor assignments. The tutorial uses the code first approach. For information on following this tutorial using the database first approach, see this Github issue.
Download or view the completed app. Download instructions.
[!INCLUDEVS prereqs]
The Visual Studio instructions use SQL Server LocalDB, a version of SQL Server Express that runs only on Windows.
[!INCLUDEVSC prereqs]
Consider downloading and installing a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.
The Visual Studio Code tab use SQLite, a cross-platform database engine.
If you run into a problem you can't resolve, compare your code to the completed project. A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.
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 based on the built-in project templates. The tutorial's focus is on how to use EF Core with ASP.NET Core, not how to customize the UI.
<a name="build"></a>
This step is optional. Building the completed app is recommended when you have problems you can't solve. If you run into a problem you can't resolve, compare your code to the completed project. Download instructions.
Select ContosoUniversity.csproj to open the project.
Build the project.
In Package Manager Console (PMC) run the following command:
Update-Database
Remove the comments from the ContosoUniversity.csproj file so SQLiteVersion is defined:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;SQLiteVersion</DefineConstants>
</PropertyGroup>
In a command window, update the database:
dotnet tool install --global dotnet-ef
dotnet ef database update
Run the project to seed the database.
Start Visual Studio 2022 and select Create a new project.
In the Create a new project dialog, select ASP.NET Core Web App, and then select Next.
In the Configure your new project dialog, enter ContosoUniversity for Project name. It's important to name the project ContosoUniversity, including matching the capitalization, so the namespaces will match when you copy and paste example code.
Select Next.
In the Additional information dialog, select .NET 6.0 (Long-term support) and then select Create.
In a terminal, navigate to the folder in which the project folder should be created.
Run the following commands to create a Razor Pages project and cd into the new project folder:
dotnet new webapp -o ContosoUniversity
cd ContosoUniversity
Copy and paste the following code into the Pages/Shared/_Layout.cshtml file:
[!code-cshtmlMain]
The layout file sets the site header, footer, and menu. The preceding code makes the following changes:
In Pages/Index.cshtml, replace the contents of the file with the following code:
[!code-cshtmlMain]
The preceding code replaces the text about ASP.NET Core with text about this app.
Run the app to verify that the home page appears.
The following sections create a data model:
A student can enroll in any number of courses, and a course can have any number of students enrolled in it.
Models/Student.cs with the following code:
[!code-csharpMain]The ID property becomes the primary key column of the database table that corresponds to this class. By default, EF Core interprets a property that's named ID or classnameID as the primary key. So the alternative automatically recognized name for the Student class primary key is StudentID. For more information, see EF Core - Keys.
The Enrollments property is a navigation property. Navigation properties hold 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 database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.
In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. For example, suppose a Student row has ID=1. Related Enrollment rows will have StudentID = 1. StudentID is a foreign key in the Enrollment table.
The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. Other collection types can be used, such as List<Enrollment> or HashSet<Enrollment>. When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.
Create Models/Enrollment.cs with the following code:
The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. For a production data model, many developers choose one pattern and use it consistently. This tutorial uses both just to illustrate that both work. Using ID without classname makes it easier to implement some kinds of data model changes.
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 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 is the foreign key 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.
Create Models/Course.cs with the following code:
[!code-csharpMain]
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 database generate it.
Build the app. The compiler generates several warnings about how null values are handled. See this GitHub issue, Nullable reference types, and Tutorial: Express your design intent more clearly with nullable and non-nullable reference types for more information.
To eliminate the warnings from nullable reference types, remove the following line from the ContosoUniversity.csproj file:
<Nullable>enable</Nullable>
The scaffolding engine currently does not support nullable reference types, therefore the models used in scaffold can't either.
Remove the ? nullable reference type annotation from public string? RequestId { get; set; } in Pages/Error.cshtml.cs so the project builds without compiler warnings.
In this section, the ASP.NET Core scaffolding tool is used to generate:
DbContext class. The context is the main class that coordinates Entity Framework functionality for a given data model. It derives from the xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName class.Student entity.SchoolContext rather than ContosoUniversityContext. The updated context name: ContosoUniversity.Data.SchoolContextThe following packages are automatically installed:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.DesignRun the following .NET CLI commands to install required NuGet packages:
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
The Microsoft.VisualStudio.Web.CodeGeneration.Design package is required for scaffolding. Although the app won't use SQL Server, the scaffolding tool needs the SQL Server package.
Create a Pages/Students folder.
Run the following command to install the aspnet-codegenerator scaffolding tool.
dotnet tool install --global dotnet-aspnet-codegenerator
Run the following command to scaffold Student pages.
On Windows
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages\Students --referenceScriptLibraries -sqlite
On macOS or Linux
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages/Students --referenceScriptLibraries -dbProvider sqlite
If the preceding step fails, build the project and retry the scaffold step.
The scaffolding process:
Create.cshtml and Create.cshtml.csDelete.cshtml and Delete.cshtml.csDetails.cshtml and Details.cshtml.csEdit.cshtml and Edit.cshtml.csIndex.cshtml and Index.cshtml.csData/SchoolContext.cs.Program.cs.appsettings.json.The scaffolding tool generates a connection string in the appsettings.json file.
The connection string specifies SQL Server LocalDB:
[!code-jsonMain]
LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. By default, LocalDB creates .mdf files in the C:/Users/<user> directory.
Rename the connection string key to SchoolContextSQLite and shorten value to CU.db:
[!code-jsonMain]
Don't change the directory without making sure it's valid.
Renaming the connection string key to SchoolContextSQLite helps the author maintain one sample that supports both the SQLlite and SQL Server.
The main class that coordinates EF Core functionality for a given data model is the database context class. The context is derived from xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName. The context specifies which entities are included in the data model. In this project, the class is named SchoolContext.
Update Data/SchoolContext.cs with the following code:
[!code-csharpMain]
The preceding code changes from the singular DbSet<Student> Student to the plural DbSet<Student> Students. To make the Razor Pages code match the new DBSet name, make a global change from:
_context.Student.
to:
_context.Students.
There are 8 occurrences.
Because an entity set contains multiple entities, many developers prefer the DBSet property names should be plural.
The highlighted code:
OnModelCreating:
SchoolContext has been initialized but before the model has been secured and used to initialize the context.Student entity will have references to the other entities.We hope to fix this issue in a future release.
ASP.NET Core is built with dependency injection. Services such as the SchoolContext are registered with dependency injection during app startup. Components that require these services, such as Razor Pages, are provided these services via constructor parameters. The constructor code that gets a database context instance is shown later in the tutorial.
The scaffolding tool automatically registered the context class with the dependency injection container.
The following highlighted lines were added by the scaffolder:
[!code-csharpMain]
Verify the code added by the scaffolder calls xref:Microsoft.EntityFrameworkCore.SqliteDbContextOptionsBuilderExtensions.UseSqlite%2A.
[!code-csharpMain]
See Use SQLite for development, SQL Server for production for information on using a production database.
The name of the connection string is passed in to the context by calling a method on a xref:Microsoft.EntityFrameworkCore.DbContextOptions object. For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json or the appsettings.Development.json file.
<a name="dbx"></a>
Add xref:Microsoft.Extensions.DependencyInjection.DatabaseDeveloperPageExceptionFilterServiceExtensions.AddDatabaseDeveloperPageExceptionFilter%2A and xref:Microsoft.AspNetCore.Builder.MigrationsEndPointExtensions.UseMigrationsEndPoint%2A as shown in the following code:
[!code-csharpMain]
Add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package.
In the Package Manager Console, enter the following to add the NuGet package:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
The Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package provides ASP.NET Core middleware for Entity Framework Core error pages. This middleware helps to detect and diagnose errors with Entity Framework Core migrations.
The AddDatabaseDeveloperPageExceptionFilter provides helpful error information in the development environment for EF migrations errors.
Update Program.cs to create the database if it doesn't exist:
[!code-csharpMain]
[!code-csharpMain]
The xref:Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated%2A method takes no action if a database for the context exists. If no database exists, it creates the database and schema. EnsureCreated enables the following workflow for handling data model changes:
EmailAddress field.EnsureCreated creates a database with the new schema.This workflow works early in development when the schema is rapidly evolving, as long as data doesn't need to be preserved. The situation is different when data that has been entered into the database needs to be preserved. When that is the case, use migrations.
Later in the tutorial series, the database is deleted that was created by EnsureCreated and migrations is used. A database that is created by EnsureCreated can't be updated by using migrations.
The EnsureCreated method creates an empty database. This section adds code that populates the database with test data.
Create Data/DbInitializer.cs with the following code:
[!code-csharpMain]
The code checks if there are any students in the database. If there are no students, it adds test data to the database. It creates the test data in arrays rather than List<T> collections to optimize performance.
Program.cs, remove // from the DbInitializer.Initialize line:[!code-csharpMain]
Stop the app if it's running, and run the following command in the Package Manager Console (PMC):
Drop-Database -Confirm
Respond with Y to delete the database.
CU.db file.Student model maps to the Student table schema.Use a SQLite tool to view the database schema and seeded data. The database file is named CU.db and is located in the project folder.
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 doing 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 can 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 return value, await keyword, and ToListAsync method make the code execute asynchronously.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
async keyword tells the compiler to:
Task return type represents ongoing work.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 the ToList extension method.Some things to be aware of when writing asynchronous code that uses EF Core:
ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.
<!-- Review: See https://github.com/dotnet/AspNetCore.Docs/issues/14528 -->[!WARNING] The async implementation of Microsoft.Data.SqlClient has some known issues (#593, #601, and others). If you're seeing unexpected performance problems, try using sync command execution instead, especially when dealing with large text or binary values.
In general, a web page shouldn't be loading an arbitrary number of rows. A query should use paging or a limiting approach. For example, the preceding query could use Take to limit the rows returned:
[!code-csharpMain]
Enumerating a large table in a view could return a partially constructed HTTP 200 response if a database exception occurs part way through the enumeration.
Paging is covered later in the tutorial.
For more information, see Performance considerations (EF).
Use SQLite for development, SQL Server for production
[!div class="step-by-step"] Next tutorial
:::moniker-end
:::moniker range="= aspnetcore-5.0"
This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. The tutorials build a web site for a fictional Contoso University. The site includes functionality such as student admission, course creation, and instructor assignments. The tutorial uses the code first approach. For information on following this tutorial using the database first approach, see this Github issue.
Download or view the completed app. Download instructions.
[!INCLUDEVS prereqs]
The Visual Studio instructions use SQL Server LocalDB, a version of SQL Server Express that runs only on Windows.
[!INCLUDEVS Code prereqs]
Consider downloading and installing a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.
The Visual Studio Code tab use SQLite, a cross-platform database engine.
If you run into a problem you can't resolve, compare your code to the completed project. A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.
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 based on the built-in project templates. The tutorial's focus is on how to use EF Core with ASP.NET Core, not how to customize the UI.
<a name="build"></a>
This step is optional. Building the completed app is recommended when you have problems you can't solve. If you run into a problem you can't resolve, compare your code to the completed project. Download instructions.
Select ContosoUniversity.csproj to open the project.
Update-Database
Remove the comments from the ContosoUniversity.csproj file so SQLiteVersion is defined:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;SQLiteVersion</DefineConstants>
</PropertyGroup>
In a command window, update the database:
dotnet tool install --global dotnet-ef
dotnet ef database update
Run the project to seed the database.
ContosoUniversity for Project name. It's important to use this exact name including capitalization, so each namespace matches when code is copied.In a terminal, navigate to the folder in which the project folder should be created.
Run the following commands to create a Razor Pages project and cd into the new project folder:
dotnet new webapp -o ContosoUniversity
cd ContosoUniversity
Copy and paste the following code into the Pages/Shared/_Layout.cshtml file:
[!code-cshtmlMain]
The layout file sets the site header, footer, and menu. The preceding code makes the following changes:
In Pages/Index.cshtml, replace the contents of the file with the following code:
[!code-cshtmlMain]
The preceding code replaces the text about ASP.NET Core with text about this app.
Run the app to verify that the home page appears.
The following sections create a data model:
A student can enroll in any number of courses, and a course can have any number of students enrolled in it.
Create a Models folder in the project folder.
Create Models/Student.cs with the following code:
[!code-csharpMain]
The ID property becomes the primary key column of the database table that corresponds to this class. By default, EF Core interprets a property that's named ID or classnameID as the primary key. So the alternative automatically recognized name for the Student class primary key is StudentID. For more information, see EF Core - Keys.
The Enrollments property is a navigation property. Navigation properties hold 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 database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.
In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. For example, suppose a Student row has ID=1. Related Enrollment rows will have StudentID = 1. StudentID is a foreign key in the Enrollment table.
The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. Other collection types can be used, such as List<Enrollment> or HashSet<Enrollment>. When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.
Create Models/Enrollment.cs with the following code:
The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. For a production data model, many developers choose one pattern and use it consistently. This tutorial uses both just to illustrate that both work. Using ID without classname makes it easier to implement some kinds of data model changes.
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 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 is the foreign key 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.
Create Models/Course.cs with the following code:
[!code-csharpMain]
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 database generate it.
Build the project to validate that there are no compiler errors.
In this section, the ASP.NET Core scaffolding tool is used to generate:
DbContext class. The context is the main class that coordinates Entity Framework functionality for a given data model. It derives from the xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName class.Student entity.SchoolContext rather than ContosoUniversityContext. The updated context name: ContosoUniversity.Data.SchoolContextIf scaffolding fails with the error 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.', run the scaffold tool again or see this GitHub issue.
The following packages are automatically installed:
Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.ToolsMicrosoft.VisualStudio.Web.CodeGeneration.DesignRun the following .NET CLI commands to install required NuGet packages:
dotnet add package Microsoft.EntityFrameworkCore.SQLite -v 5.0.0-*
dotnet add package Microsoft.EntityFrameworkCore.SqlServer -v 5.0.0-*
dotnet add package Microsoft.EntityFrameworkCore.Design -v 5.0.0-*
dotnet add package Microsoft.EntityFrameworkCore.Tools -v 5.0.0-*
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design -v 5.0.0-*
dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore -v 5.0.0-*
The Microsoft.VisualStudio.Web.CodeGeneration.Design package is required for scaffolding. Although the app won't use SQL Server, the scaffolding tool needs the SQL Server package.
Create a Pages/Students folder.
Run the following command to install the aspnet-codegenerator scaffolding tool.
dotnet tool install --global dotnet-aspnet-codegenerator
Run the following command to scaffold Student pages.
On Windows
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages\Students --referenceScriptLibraries -sqlite
On macOS or Linux
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages/Students --referenceScriptLibraries -dbProvider sqlite
If the preceding step fails, build the project and retry the scaffold step.
The scaffolding process:
Create.cshtml and Create.cshtml.csDelete.cshtml and Delete.cshtml.csDetails.cshtml and Details.cshtml.csEdit.cshtml and Edit.cshtml.csIndex.cshtml and Index.cshtml.csData/SchoolContext.cs.Startup.cs.appsettings.json.The scaffolding tool generates a connection string in the appsettings.json file.
The connection string specifies SQL Server LocalDB:
[!code-jsonMain]
LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. By default, LocalDB creates .mdf files in the C:/Users/<user> directory.
Shorten the SQLite connection string to CU.db:
[!code-jsonMain]
Don't change the directory without making sure it's valid.
The main class that coordinates EF Core functionality for a given data model is the database context class. The context is derived from xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName. The context specifies which entities are included in the data model. In this project, the class is named SchoolContext.
Update Data/SchoolContext.cs with the following code:
[!code-csharpMain]
The preceding code changes from the singular DbSet<Student> Student to the plural DbSet<Student> Students. To make the Razor Pages code match the new DBSet name, make a global change from:
_context.Student.
to:
_context.Students.
There are 8 occurrences.
Because an entity set contains multiple entities, many developers prefer the DBSet property names should be plural.
The highlighted code:
OnModelCreating:
SchoolContext has been initialized but before the model has been secured and used to initialize the context.Student entity will have references to the other entities.Build the project to verify there are no compiler errors.
ASP.NET Core is built with dependency injection. Services such as the SchoolContext are registered with dependency injection during app startup. Components that require these services, such as Razor Pages, are provided these services via constructor parameters. The constructor code that gets a database context instance is shown later in the tutorial.
The scaffolding tool automatically registered the context class with the dependency injection container.
The following highlighted lines were added by the scaffolder:
[!code-csharpMain]
Verify the code added by the scaffolder calls UseSqlite.
[!code-csharpMain]
See Use SQLite for development, SQL Server for production for information on using a production database.
The name of the connection string is passed in to the context by calling a method on a xref:Microsoft.EntityFrameworkCore.DbContextOptions object. For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.
<a name="dbx"></a>
Add xref:Microsoft.Extensions.DependencyInjection.DatabaseDeveloperPageExceptionFilterServiceExtensions.AddDatabaseDeveloperPageExceptionFilter%2A and xref:Microsoft.AspNetCore.Builder.MigrationsEndPointExtensions.UseMigrationsEndPoint%2A as shown in the following code:
[!code-csharpMain]
Add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package.
In the Package Manager Console, enter the following to add the NuGet package:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
[!code-csharpMain]
The Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package provides ASP.NET Core middleware for Entity Framework Core error pages. This middleware helps to detect and diagnose errors with Entity Framework Core migrations.
The AddDatabaseDeveloperPageExceptionFilter provides helpful error information in the development environment for EF migrations errors.
Update Program.cs to create the database if it doesn't exist:
[!code-csharpMain]
The xref:Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated%2A method takes no action if a database for the context exists. If no database exists, it creates the database and schema. EnsureCreated enables the following workflow for handling data model changes:
EmailAddress field.EnsureCreated creates a database with the new schema.This workflow works early in development when the schema is rapidly evolving, as long as data doesn't need to be preserved. The situation is different when data that has been entered into the database needs to be preserved. When that is the case, use migrations.
Later in the tutorial series, the database is deleted that was created by EnsureCreated and migrations is used. A database that is created by EnsureCreated can't be updated by using migrations.
The EnsureCreated method creates an empty database. This section adds code that populates the database with test data.
Create Data/DbInitializer.cs with the following code:
[!code-csharpMain]
The code checks if there are any students in the database. If there are no students, it adds test data to the database. It creates the test data in arrays rather than List<T> collections to optimize performance.
In Program.cs, remove // from the DbInitializer.Initialize line:
context.Database.EnsureCreated();
DbInitializer.Initialize(context);
Stop the app if it's running, and run the following command in the Package Manager Console (PMC):
Drop-Database -Confirm
Respond with Y to delete the database.
CU.db file.Student model maps to the Student table schema.Use a SQLite tool to view the database schema and seeded data. The database file is named CU.db and is located in the project folder.
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 doing 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 can 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 return value, await keyword, and ToListAsync method make the code execute asynchronously.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
async keyword tells the compiler to:
Task return type represents ongoing work.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 the ToList extension method.Some things to be aware of when writing asynchronous code that uses EF Core:
ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.
<!-- Review: See https://github.com/dotnet/AspNetCore.Docs/issues/14528 -->In general, a web page shouldn't be loading an arbitrary number of rows. A query should use paging or a limiting approach. For example, the preceding query could use Take to limit the rows returned:
[!code-csharpMain]
Enumerating a large table in a view could return a partially constructed HTTP 200 response if a database exception occurs part way through the enumeration.
xref:Microsoft.AspNetCore.Mvc.MvcOptions.MaxModelBindingCollectionSize defaults to 1024. The following code sets MaxModelBindingCollectionSize:
[!code-csharpMain]
See Configuration for information on configuration settings like MyMaxModelBindingCollectionSize.
Paging is covered later in the tutorial.
For more information, see Performance considerations (EF).
[!INCLUDEs]
Use SQLite for development, SQL Server for production
[!div class="step-by-step"] Next tutorial
:::moniker-end
:::moniker range="< aspnetcore-5.0"
This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. The tutorials build a web site for a fictional Contoso University. The site includes functionality such as student admission, course creation, and instructor assignments. The tutorial uses the code first approach. For information on following this tutorial using the database first approach, see this Github issue.
Download or view the completed app. Download instructions.
[!INCLUDEVS prereqs]
[!INCLUDEVS Code prereqs]
The Visual Studio instructions use SQL Server LocalDB, a version of SQL Server Express that runs only on Windows.
The Visual Studio Code instructions use SQLite, a cross-platform database engine.
If you choose to use SQLite, download and install a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.
If you run into a problem you can't resolve, compare your code to the completed project. A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.
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 based on the built-in project templates. The tutorial's focus is on how to use EF Core, not how to customize the UI.
Follow the link at the top of the page to get the source code for the completed project. The cu30 folder has the code for the ASP.NET Core 3.0 version of the tutorial. Files that reflect the state of the code for tutorials 1-7 can be found in the cu30snapshots folder.
To run the app after downloading the completed project:
Build the project.
In Package Manager Console (PMC) run the following command:
Update-Database
Run the project to seed the database.
To run the app after downloading the completed project:
Delete ContosoUniversity.csproj, and rename ContosoUniversitySQLite.csproj to ContosoUniversity.csproj.
In Program.cs, comment out #define Startup so StartupSQLite is used.
Delete appsettings.json, and rename appSettingsSQLite.json to appsettings.json.
Delete the Migrations folder, and rename MigrationsSQL to Migrations.
Do a global search for #if SQLiteVersion and remove #if SQLiteVersion and the associated #endif statement.
Build the project.
At a command prompt in the project folder, run the following commands:
dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef --version 5.0.0-*
dotnet ef database update
In your SQLite tool, run the following SQL statement:
UPDATE Department SET RowVersion = randomblob(8)
Run the project to seed the database.
In a terminal, navigate to the folder in which the project folder should be created.
Run the following commands to create a Razor Pages project and cd into the new project folder:
dotnet new webapp -o ContosoUniversity
cd ContosoUniversity
Set up the site header, footer, and menu by updating Pages/Shared/_Layout.cshtml:
Change each occurrence of "ContosoUniversity" to "Contoso University". There are three occurrences.
Delete the Home and Privacy menu entries, and add entries for About, Students, Courses, Instructors, and Departments.
The changes are highlighted.
[!code-cshtmlMain]
In Pages/Index.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET Core with text about this app:
[!code-cshtmlMain]
Run the app to verify that the home page appears.
The following sections create a data model:
A student can enroll in any number of courses, and a course can have any number of students enrolled in it.
Create a Models folder in the project folder.
Create Models/Student.cs with the following code:
[!code-csharpMain]
The ID property becomes the primary key column of the database table that corresponds to this class. By default, EF Core interprets a property that's named ID or classnameID as the primary key. So the alternative automatically recognized name for the Student class primary key is StudentID. For more information, see EF Core - Keys.
The Enrollments property is a navigation property. Navigation properties hold 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 database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.
In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. For example, suppose a Student row has ID=1. Related Enrollment rows will have StudentID = 1. StudentID is a foreign key in the Enrollment table.
The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. You can use other collection types, such as List<Enrollment> or HashSet<Enrollment>. When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.
Create Models/Enrollment.cs with the following code:
[!code-csharpMain]
The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. For a production data model, choose one pattern and use it consistently. This tutorial uses both just to illustrate that both work. Using ID without classname makes it easier to implement some kinds of data model changes.
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 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 is the foreign key 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.
Create Models/Course.cs with the following code:
[!code-csharpMain]
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 database generate it.
Build the project to validate that there are no compiler errors.
In this section, you use the ASP.NET Core scaffolding tool to generate:
Microsoft.EntityFrameworkCore.DbContext class.Student entity.The following packages are automatically installed:
Microsoft.VisualStudio.Web.CodeGeneration.DesignMicrosoft.EntityFrameworkCore.SqlServerMicrosoft.Extensions.Logging.DebugMicrosoft.EntityFrameworkCore.Toolsdotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.Extensions.Logging.Debug
The Microsoft.VisualStudio.Web.CodeGeneration.Design package is required for scaffolding. Although the app won't use SQL Server, the scaffolding tool needs the SQL Server package.
Create a Pages/Students folder.
Run the following command to install the aspnet-codegenerator scaffolding tool.
dotnet tool install --global dotnet-aspnet-codegenerator
Run the following command to scaffold Student pages.
On Windows
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages\Students --referenceScriptLibraries
On macOS or Linux
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages/Students --referenceScriptLibraries
If you have a problem with the preceding step, build the project and retry the scaffold step.
The scaffolding process:
Create.cshtml and Create.cshtml.csDelete.cshtml and Delete.cshtml.csDetails.cshtml and Details.cshtml.csEdit.cshtml and Edit.cshtml.csIndex.cshtml and Index.cshtml.csData/SchoolContext.cs.Startup.cs.appsettings.json.The appsettings.json file specifies the connection string SQL Server LocalDB.
[!code-jsonMain]
LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. By default, LocalDB creates .mdf files in the C:/Users/<user> directory.
Change the connection string to point to a SQLite database file named CU.db:
[!code-jsonMain]
The main class that coordinates EF Core functionality for a given data model is the database context class. The context is derived from xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName. The context specifies which entities are included in the data model. In this project, the class is named SchoolContext.
Update Data/SchoolContext.cs with the following code:
[!code-csharpMain]
The highlighted code creates a xref:Microsoft.EntityFrameworkCore.DbSet%601 property for each entity set. In EF Core terminology:
Since an entity set contains multiple entities, the DBSet properties should be plural names. Since the scaffolding tool created aStudent DBSet, this step changes it to plural Students.
To make the Razor Pages code match the new DBSet name, make a global change across the whole project of _context.Student to _context.Students. There are 8 occurrences.
Build the project to verify there are no compiler errors.
ASP.NET Core is built with dependency injection. Services (such as the EF Core database 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 database context instance is shown later in the tutorial.
The scaffolding tool automatically registered the context class with the dependency injection container.
In ConfigureServices, the highlighted lines were added by the scaffolder:
[!code-csharpMain]
In ConfigureServices, make sure the code added by the scaffolder calls UseSqlite.
[!code-csharpMain]
The name of the connection string is passed in to the context by calling a method on a xref:Microsoft.EntityFrameworkCore.DbContextOptions object. For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.
Update Program.cs to create the database if it doesn't exist:
[!code-csharpMain]
The xref:Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated%2A method takes no action if a database for the context exists. If no database exists, it creates the database and schema. EnsureCreated enables the following workflow for handling data model changes:
EmailAddress field.EnsureCreated creates a database with the new schema.This workflow works well early in development when the schema is rapidly evolving, as long as you don't need to preserve data. The situation is different when data that has been entered into the database needs to be preserved. When that is the case, use migrations.
Later in the tutorial series, you delete the database that was created by EnsureCreated and use migrations instead. A database that is created by EnsureCreated can't be updated by using migrations.
The EnsureCreated method creates an empty database. This section adds code that populates the database with test data.
Create Data/DbInitializer.cs with the following code:
[!code-csharpMain]
The code checks if there are any students in the database. If there are no students, it adds test data to the database. It creates the test data in arrays rather than List<T> collections to optimize performance.
In Program.cs, replace the EnsureCreated call with a DbInitializer.Initialize call:
// context.Database.EnsureCreated();
DbInitializer.Initialize(context);
Stop the app if it's running, and run the following command in the Package Manager Console (PMC):
Drop-Database
CU.db file.Restart the app.
Select the Students page to see the seeded data.
Student model maps to the Student table schema.Use your SQLite tool to view the database schema and seeded data. The database file is named CU.db and is located in the project folder.
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 can 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.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
async keyword tells the compiler to:
Task<T> return type represents ongoing work.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 the ToList extension method.Some things to be aware of when writing asynchronous code that uses EF Core:
ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.
[!div class="step-by-step"] Next tutorial
:::moniker-end