Creating a Web API with Entity Framework Core and Code First
IT Tips & Insights: One of Softensity’s Senior Software Engineers walks you through the steps to create an ASP.Net web API.
By Ejder Kuvemli, Software Engineer
In this article, we’re going to create an ASP.Net web API for a very simple photo-sharing web application with Entity Framework Core and Code First using a Microsoft SQL Server database. We’ll also use Entity Framework’s Fluent API to configure and map the types and properties within this article.
To follow all the steps, use either .Net CLI or Visual Studio (Code or 2022). Find prerequisites and download links below.
Visual Studio 2022 Community Edition or Visual Studio Code
Creating an ASP.Net Web API Project with .Net CLI
Let’s use EFShare as the project name. To create the project, simply use the following commands:
dotnet new webapi -minimal -o EFShare
Since we are going to use Entity Framework Core as the ORM to access data, after creating the project we need to add Microsoft.EntityFrameworkCore package to our project.
dotnet add package Microsoft.EntityFrameworkCore
Before diving into creating the database, you’re going to need another tool for scaffolding and migrating the database. So, install dotnet-ef tool with the following command:
dotnet tool install --global dotnet-ef
Creating the Model
Prior to creating a DbContext for the API, let’s talk about which tables and fields we are going to create on the database. Create a folder named Context under the solution directory. Define the model (in separate classes) and the DbContext within this folder.
“Use virtual keyword for navigation properties which enables lazy-loading. For now, we are going to create only the simplest version of our model.”
Below you can find the initial model and the tables we are going to create, and also some explanations to them:
The table where we store a user’s data, like FullName, Usernameand the UserId. Later in this article we need to add some additional fields.
To store user settings (for example, preferred Theme as dark or light), we are going to use this table.
This table is used to store user Post data. For now, it only contains a single property CreatedUserId.
A table stores the physical location of a media file of a specific post. A post can contain more than one media. PostId and Path are needed properties.
The table is used to store user comments for a specific post. ParentCommentId will be used when a user replies to a comment.
We are going to create our DbContext based on the model we have created and expose some DbSets to query and save data.
“It is important to mention here that we use DbContext from Microsoft.EntityFrameworkCore namespace with the latest version of Entity Framework and not System.Data.Entity.”
Creating the Database
We have already installed the dotnet-ef tool and we are going to use this tool to create the database. Let’s try running the following command:
dotnet ef database update
As you can see it fails to update or create the database. The error you see should be a message like the below:
Your startup project 'EFShare' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is correct, install the package, and try again.
To prevent this, we need to install Microsoft.EntityFrameworkCore.Design package with the following command:
dotnet add package Microsoft.EntityFrameworkCore.Design
When you run the database update command again, you should see the following error message as the output now:
No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
To solve this, Microsoft.EntityFrameworkCore.SqlServer package must be installed first with the following command:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
And then you need to add database context and connection string to the application service provider. In order to do this simply add the below lines where you configure the services in Program.cs:
If you try to update again, you cannot update the database again because of the following warning (and also, the same error message above):
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: 'AddDbContext' was called with configuration, but the context type 'EFShareContext' only declares a parameterless constructor. This means that the configuration passed to 'AddDbContext' will never be used. If configuration is passed to 'AddDbContext', then 'EFShareContext' should declare a constructor that accepts a DbContextOptions<EFShareContext> and must pass it to the base constructor for DbContext.
To fix this you need to create a new parameterized constructor in our DbContext modifying EFShareContext.cs like below:
After adding this, if you run the database update command again, everything should work as expected.
The database has been created but if you go and check it, you should not see any table other than __EFMigrationsHistory.
This is because we did not create a migration to initialize the database and there is no relationship or reference/navigation properties within the model. For creating an initial migration, we run the command below:
dotnet ef migrations add InitialMigration
After running this command, you should see a new folder named Migrations that has been created under the solution. If you expand the folder, there must be 3 new classes.
[timetamp]_InitialMigration.cs: Contains methods Up() and Down(). Up is the method which will update the database with your new changes and if you want to roll back or restore the database to the previous state you can see the updates for it within the Down() method.
[timetamp]_InitialMigration.Designer.cs: Entity Framework uses the migration metadata information in this file.
EFShareContextModelSnapshot.cs: The snapshot of the current model.
And run the following database update command again:
dotnet ef database update
This updates the database with the latest migration you have created. If you want to use a specific migration you need to specify it with the above method.
We created the model and the DbContext and we also have a migration to update the database, but we still need some relationships between the tables and objects. To achieve this, we will use Fluent API and create some relationships between objects and update the database again.
In order to access Fluent API, you must first create the following method in EFShareContext.cs:
We will define and use Fluent API within this method. Let’s do this step by step.
As you can see, Comment has a ParentCommentId property. When a user sends a comment for a post, another user can reply to that comment, so the first comment is the parent and any reply to that comment will be the child. Let’s add the following navigation properties to Comment.cs
We are going to use Users and UserSettings tables as an example to this. Let’s modify our User and UserSetting class. First, add the following navigation properties to the specified classes.
A post may contain several media files, so we are going to use these two tables as an example here. Add the following navigation properties:
We do not have a specific example for this now, but let’s create a Followers table to store which user follows the other.
After this we need to modify our DbContext as well by adding the following DbSet.
The Followers table has two fields: FollowingUserId and FollowerUserId. We are going to create a many-to-many relationship between users. In order to do that we use the navigation properties defined above. If we modify OnModelCreating method for this, use:
Updating the Database
We have added some relationships through Fluent API and now we need to create another migration and update the database. First we run the command:
dotnet ef migrations add FluentAPIUpdates
After creating the migration, we can now update the database:
dotnet ef database update
Now you should see the following error message:
Introducing FOREIGN KEY constraint 'FK_Followers_Users_FollowingUserId' on table 'Followers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
This is a typical error. When you try to delete some data, it may cascade directly to the other entities if they are related. To prevent this, we need to replace the Fluent API declaration of the Follower entity with the below:
We can remove the latest migration and add it again then update the database by running the following command:
dotnet ef migrations remove
And add a migration again:
dotnet ef migration add FluentAPIUpdates
dotnet ef database update
Everything should work as expected. If you create a new diagram with the management studio you should see the diagram below:
My name is Ejder Kuvemli and I am a Senior Software Engineer at Softensity. I consider myself a backend developer but I've also worked with a variety of frontend technologies throughout my career. Thank you for taking the time to read my article.