While working with the EF Core Code-First approach, we create the classes for our domain entities first. Later, we’ll create the database from our code by using migrations. This is the opposite of the Database-First approach where we design our database first and then create the classes which match our database design.
In the EF Core Code-First approach, we have full control over the code and the database is just a store without any logic. This approach is helpful in situations where we are starting with the development of a new project and we don’t have a clear picture of what our database should look like yet.
We don’t have to worry about creating and updating the database. We can just make the changes in our code and then sync everything easily with the database. The important thing to note is that the manual changes that we make to the database could be lost during migration. So we should make changes to the code only.
The source code for this blog post can be found on this Github repo.
Let’s have a look at how to create a .NET Core Web API application with the EF Core Code-First approach.
The article is divided into the following sections:
- Setting Up the ASP.NET Core Web API Project
- Configuring EF Core
- Seeding Data, Creating DB Script and Reverting Migrations
- Creating the Repository
- Creating the API Controller
- Testing the API
Setting Up the ASP.NET Core Web API Project
As a first step, let’s set up an ASP.NET Core Web API Project. We have explained this in detail in one of our other articles: Creating and configuring a new ASP.NET Core Web API project
The article linked above covers a lot of additional topics. You may go through the entire article if you want to, but the section linked above is quite enough to follow along with this article.
Following the article linked above, let’s create a new project called EFCoreCodeFirstSample
Configuring EF Core
Once we have set up the project, the next step is to set up the EF Core.
Following are the steps for configuring the EF Core:
Defining the Model
First, let’s define the model. We will start by creating a folderModels
within the root of the application.
Let’s add a new class Employee.cs
inside:
using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace EFCoreCodeFirstSample.Models { public class Employee { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public long EmployeeId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime DateOfBirth { get; set; } public string PhoneNumber { get; set; } public string Email { get; set; } } }
The code above defines the classEmployee
with some properties. Additionally, we have decorated the EmployeeId
property with Key
and DatabaseGenerated
attributes. We did this because we will be converting this class into a database table and the columnEmployeeId
will serve as our primary key with the auto-incremented identity.
Creating a Context File
As the next step, let’s create a context class, define database connection and register the context. The process is explained in detail in one of our other articles: Context Class and the Database Connection
Following the above article, let’s define the context file EmployeeContext.cs
(it requires installed Microsoft.EntityFrameworkCore 3.0.0
package):
using Microsoft.EntityFrameworkCore; namespace EFCoreCodeFirstSample.Models { public class EmployeeContext : DbContext { public EmployeeContext(DbContextOptions options) : base(options) { } public DbSet<Employee> Employees { get; set; } } }
and let’s define the database connection in the appsettings.json
file as:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "ConnectionString": { "EmployeeDB": "server=MY_SERVER;database=EmployeeDB;User ID=MY_USER;password=MY_PASSWORD;" }, "AllowedHosts": "*" }
Of course, modify the ConnectionString
property to match with that of ours.
Then let’s install the Microsoft.EntityFrameworkCore.SqlServer
package and register our context in the Startup.cs
:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<EmployeeContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:EmployeeDB"])); services.AddControllers(); }
Generating the Database from Code Using Migrations
Our next step is to add Code-First Migrations. Migrations automate the creation of database based on our Model. The EF Core packages required for migration will be added with .NET Core project setup.
Let’s install the Microsoft.EntityFrameworkCore.Tools
package and run the following command in the Package Manager console:
PM> Add-Migration EFCoreCodeFirstSample.Models.EmployeeContext
This will create the classes for supporting migrations.
Now let’s apply those changes to the database.
Run the following command:
PM> update-database
This will update the database based on our models.
Now let’s verify that the database and tables are created by opening SQL Server Management Studio or Visual Studio Server Explorer:
We can see the database EmployeeDB
is created with a table Employees
which contains the columns based on the fields we defined in our model.
Each time we make changes to our entities and do a migration, we can see new migration files created in our solution and new entries in the table__EFMigrationsHistory
.
When using the EF Core Code-First approach the best practice is to make all modifications to the database through the model and then update the database by doing the migration. Ideally, we should not make any manual changes to the database.
With that, the EF Core setup is complete.
Seeding Data, Reverting Migrations and Creating DB Scripts
Seeding Data
Data seeding allows us to provide initial data during the creation of a database. Then, EF Core migrations will automatically determine what insert, update or delete operations need to be applied when upgrading the database to a new version of the model.
So let’s create some seed data now. For this, we need to override the OnModelCreating
method in the EmployeeContext
class:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Employee>().HasData(new Employee { EmployeeId = 1, FirstName = "Uncle", LastName = "Bob", Email = "[email protected]", DateOfBirth = new DateTime(1979, 04, 25), PhoneNumber = "999-888-7777" }, new Employee { EmployeeId = 2, FirstName = "Jan", LastName = "Kirsten", Email = "[email protected]", DateOfBirth = new DateTime(1981, 07, 13), PhoneNumber = "111-222-3333" }); }
Here we have provided two Employee
records that will be inserted into the database as part of the migration.
Let’s run the migration commands once again:
Add-Migration EFCoreCodeFirstSample.Models.EmployeeContextSeed
update-database
This will create a new migration file in our Migrations
folder and update the database with the seed data we provided:
Now the Employee table in our database will look like this:
Reverting Migrations
After making changes to our EF Core model, the database schema will be out of sync. To bring it to sync with the model, let’s add another migration.
Let’s add a new property Gender
in our employee model and then do a migration.
It is a good practice to give meaningful names to the migration like a commit message in a version control system. For example, if we add a new field Gender
to the Employee
model, we may give a name like AddEmployeeGender
.
Add-Migration EFCoreCodeFirstSample.Models.AddEmployeeGender
Sometimes we add a migration and then realize we need to make additional changes to our model before applying it. To remove the last migration, we can use the command:
Remove-Migration
If we already applied a migration (or several migrations) to the database but need to revert it, we can use the same command to apply migrations, but specify the name of the migration we want to roll back to.
Let’s say we already applied the migration to add the Gender column to the database by using the below command.
update-database
Now we can see the new column Gender
added to the Employee table:
Now let’s say we want to revert this migration. We can use the same command by specifying the name of the previous migration:
update-database EFCoreCodeFirstSample.Models.EmployeeContextSeed
Once this is executed, we can see that the column Gender
is removed from the Employee table:
We should remove the Gender
property from the Employee
class as well.
Creating DB Scripts
While deploying our migrations to a production database, it’s useful to generate a SQL script. We can further tune the script to match the production database. Also, we can use the script along with various deployment tools.
The command to generate the script is:
Script-Migration
Once we apply this command, we can see a SQL script generated with all changes related to our migrations.
Recommendation
If you want to learn in great detail about Entity Framework Core and many of its features, we recommend going through our Entity Framework Core series. Through the entire series, we talk about different EF Core features, from the Context classes and DbSet properties, relationships and non-relational configurations, additional migration information and querying the database. If you want, you have a place to learn a lot more about this topic.
Creating the Repository
Now that we have configured the EF Core, we need a mechanism to access the data context from our API. Directly accessing the context methods from the API controller is a bad practice and we should avoid that.
So let’s implement a simple data repository using the repository pattern. We have explained this pattern in detail in one of our other articles: Implementing the repository pattern.
Let’s add a new folder under Models and name itRepository
. Then let’s create a new interface calledIDataRepository:
namespace EFCoreCodeFirstSample.Models.Repository { public interface IDataRepository<TEntity> { IEnumerable<TEntity> GetAll(); TEntity Get(long id); void Add(TEntity entity); void Update(TEntity dbEntity, TEntity entity); void Delete(TEntity entity); } }
We will later inject this interface into our API Controller and API will be communicating with the data context using this interface.
Next, let’s create a concrete class that implements the interfaceIDataRepository
. Let’s add a new folder under Models calledDataManager
. Then let’s create a new class EmployeeManager
:
using System.Collections.Generic; using System.Linq; using EFCoreCodeFirstSample.Models.Repository; namespace EFCoreCodeFirstSample.Models.DataManager { public class EmployeeManager : IDataRepository<Employee> { readonly EmployeeContext _employeeContext; public EmployeeManager(EmployeeContext context) { _employeeContext = context; } public IEnumerable<Employee> GetAll() { return _employeeContext.Employees.ToList(); } public Employee Get(long id) { return _employeeContext.Employees .FirstOrDefault(e => e.EmployeeId == id); } public void Add(Employee entity) { _employeeContext.Employees.Add(entity); _employeeContext.SaveChanges(); } public void Update(Employee employee, Employee entity) { employee.FirstName = entity.FirstName; employee.LastName = entity.LastName; employee.Email = entity.Email; employee.DateOfBirth = entity.DateOfBirth; employee.PhoneNumber = entity.PhoneNumber; _employeeContext.SaveChanges(); } public void Delete(Employee employee) { _employeeContext.Employees.Remove(employee); _employeeContext.SaveChanges(); } } }
The classEmployeeManager
handles all database operations related to the employee. The purpose of this class is to separate the actual data operations logic from our API Controller.
This class has the following methods for supporting CRUD operations:
GetAll()
– Gets all employee records from the database.
Get()
– Gets a specific employee record from the database by passing an Id.
Add()
– Creates a new employee record in the database.
Update()
– Updates a specific employee record in the database.
Delete()
– Removes a specific employee record from the database based on the Id.
As a next step, let’s configure the repository using dependency injection. This can be done in the ConfigureServices
method in the Startup.cs
as below:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<EmployeeContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:EmployeeDB"])); services.AddScoped<IDataRepository<Employee>, EmployeeManager>(); services.AddControllers(); }
Creating the API Controller
Now that our DataManager
is all set, let’s create the API Controller and create the endpoints for handling CRUD operations. We have covered this in detail in one of our other articles: Creating a .NET Core Web API Controller.
Following the above article, let’s create the EmployeeController
class in the Controllers
folder as below:
using System.Collections.Generic; using EFCoreCodeFirstSample.Models; using EFCoreCodeFirstSample.Models.Repository; using Microsoft.AspNetCore.Mvc; namespace EFCoreCodeFirstSample.Controllers { [Route("api/employee")] [ApiController] public class EmployeeController : ControllerBase { private readonly IDataRepository<Employee> _dataRepository; public EmployeeController(IDataRepository<Employee> dataRepository) { _dataRepository = dataRepository; } // GET: api/Employee [HttpGet] public IActionResult Get() { IEnumerable<Employee> employees = _dataRepository.GetAll(); return Ok(employees); } // GET: api/Employee/5 [HttpGet("{id}", Name = "Get")] public IActionResult Get(long id) { Employee employee = _dataRepository.Get(id); if (employee == null) { return NotFound("The Employee record couldn't be found."); } return Ok(employee); } // POST: api/Employee [HttpPost] public IActionResult Post([FromBody] Employee employee) { if (employee == null) { return BadRequest("Employee is null."); } _dataRepository.Add(employee); return CreatedAtRoute( "Get", new { Id = employee.EmployeeId }, employee); } // PUT: api/Employee/5 [HttpPut("{id}")] public IActionResult Put(long id, [FromBody] Employee employee) { if (employee == null) { return BadRequest("Employee is null."); } Employee employeeToUpdate = _dataRepository.Get(id); if (employeeToUpdate == null) { return NotFound("The Employee record couldn't be found."); } _dataRepository.Update(employeeToUpdate, employee); return NoContent(); } // DELETE: api/Employee/5 [HttpDelete("{id}")] public IActionResult Delete(long id) { Employee employee = _dataRepository.Get(id); if (employee == null) { return NotFound("The Employee record couldn't be found."); } _dataRepository.Delete(employee); return NoContent(); } } }
That’s it. We have successfully created a Web API controller with endpoints for handling CRUD operations.
Testing the API
Now let’s do a quick round of testing around our API endpoints using Postman.
First, let’s create a new Employee using a Post
request:
Next, let’s do a Get
request to get all Employees. We can see the new Employee record which was created in the previous request:
Now, let’s do a Put
request to test the update functionality by changing the last name:
Once again let’s do a Get
request and verify that the last name has changed:
Now that we have successfully tested the API endpoints, let’s verify that the changes we made are actually persisted in the database. Let’s open the SQL Server management studio and verify that the record is created in the Employee
table:
Conclusion
Well, that’s all for now. In this article, we have learned the following topics.
- EF Core Code-First approach and when to use it
- Setting up a .NET Core Web API project with EF Core Code-First approach
- Creating a database from code by using migrations
- Setting up a repository to handle communication between API and the data context
- Create API endpoints for handling CRUD operations and testing them
Hope you enjoyed the article. Happy programming!
public EmployeesController(IDataRepository<Employee> dataRepository)
it will show the error msg
Can you describe the error message? And have you registered this repository as a service: services.AddScoped<IDataRepository<Employee>, EmployeeManager>();
How we can add the stored procedure ?
Hello.
In this article https://code-maze.com/migrations-and-seed-data-efcore/ you can find how to add custom code in your migration files.
Can you please share the angular application code for this api sample?
Hi, I have an “Possible null refrence return” error in “EmployeeManager.cs”, in the Get function.
Could you help me with this error?
I am using .net 6.0 (Not sure if this could cause it, have had a bit of errors because of this but it’s all solved, only this one I am having trouble with).
how can i call through ajax kindly guide me please
Hi Maze,
Thank you for the Very good example, I am new to web api, I have understood the reference creation of repository as services.AddScoped<IDataRepository<Employee>, EmployeeManager>();
but where is the reference created for EmployeeContext.
Regarding registration of the context class, you can read more in this article under the Creating a Context File section. You can always download our source code to find everything you need.
This is the blog which I was looking for since last 4 days , now finally got it. Thanks man ,it is one of the best one which explains code-first approach in a way that even a layman can understand. That’s called teaching!!!
Thank you Aniket a lot. I’m glad this article helped you to learn something new. Also, I hope you will find our other articles helpful as well.
Great tutorial, I am a bit confused when it comes to using a generic interface with defined data managers as opposed to a service-esque file which accesses the db-context. Most of my operations don’t just fall into either GetByID or GetAll, but instead require extra querying logic (date ranges, filtering, etc.) and as such I wouldn’t have much use for the interface. Does this seem okay? As long as the db-context isn’t in the controller right?
Hello Ben. You are right about that and it seems ok. But let me share with you our article about Repository Pattern, where we cover that in more details: https://code-maze.com/net-core-web-development-part4/ . Here you can find how to create it in more advanced way and additionally in every user repository class you can extend the generic logic with your requirements. Of course, as you said, you don’t have to use this pattern, as long as you have any service around the context object. It has nothing to do inside the controller.
Good for including the repository and testing, but I feel the “working from home” clip-art may be a tad bit unrealistic.
Hello Jason, I am not sure that I follow you. What do you mean by “working from home clip-art may be a tad bit unrealistic”?
The stock photo of the woman in her pj’s. Just a little too perfect for a real world scenario! 😉
Oh I get it 😀 😀 Well… who knows 😀
Excellent tutorial, I enjoyed it a lot 🙂
Thank you a lot. I hope you will enjoy our other articles and series.
The update method in EmployeeManager class, What if i have some 50+ properties and wanted to do an update? is that i have to mention all the properties like that? Or is there any other easier way?
Hello. No you don’t have to do it like that. This is a simple example so we didn’t want to complicate it additionally. But the best way for operations like that (mapping from one to another entity) is to use AutoMapper library. It is a great library and speeds up the process for sure. Also it makes your code cleaner. Just search AutoMapper in ASP.NET Core and you will find instructions, it is quite easy to use. Best regards.
Yes, i’m using to to convert the model to viewModel, but is that okay to use the same for this mapping too?
Yes it is. Feel free to use it as much as you can, whenever you have some mapping actions in your project.
Will do, thanks.
Marinko, Like your tutorials. Always the best. Thanks. Can I ask if you can help on how to host the web api in Azure? Basically I want to simulate a scenario where I do all the development in my local machine and when it is done, I want to host the api + database in Azure with all the migrations. In this instance, how will my connection string look when it will be ready to be hosted in Azure. How can we run migrations in Azure?
Hello Venky, don’t mind me answering this one instead of marinko. With his blessings of course 😀
Marinko has provided a link for you that can help you with hosting of the ASP.NET Core app on Azure:
https://docs.microsoft.com/en-us/aspnet/core/tutorials/publish-to-azure-webapp-using-vs?view=aspnetcore-2.2
You can simulate the whole thing on Azure. You need to deploy the app, provision the database and then use that connection string instead of the one you’ve been using until now. You’ll see the connection string in the database description once you provision it.
Once you’ve set everything up, you can run migrations from your local machine using a remote database connection string, there is no difference.
Here are some examples of connection strings and how to query remote databases on Azure:
https://docs.microsoft.com/en-us/azure/sql-database/sql-database-connect-query-dotnet-core
Hope that helps. If you need any more help, do feel free to write.
This is the best tutorial I have ever seen. rly it’s ver greatful and usefull
Thank you very much. It is always a great pleasure to hear something like that from the reader. We hope you can find more useful materials on our site.
This is the best tutorial I have ever seen
I don’t understand the last two lines of the interface. If the interface should be about any entity, why do delete and update specifically mention employee?
Thank you Jim. It is fixed. Somehow, we didn’t see that.
Hi Maze, GetAll(); GetAll();
Thank you for this great post.
I tried ur example and add another Get in the IDataRepository :
public interface IDataRepository
{
IEnumerable
TEntity GetById(long id);//Modified
TEntity GetByMail(string mail);//Added
void Add(TEntity entity);
void Update(Employee employe, TEntity entity);
void Delete(Employee employe);
}
I used the route to call the relevant Get.
I added a new class in the model, named Course. I updated the model, but I don’t know how to proceed with the IDataRepository interface. Should I have this :
public interface IDataRepository
{
IEnumerable
TEntity GetById(long id);
TEntity GetByMail(string mail);
TEntity GetByCourseName(string name); //Added
void Add(TEntity entity);
void UpdateEmployee(Employee employe, TEntity entity);//Modified
void UpdateCourse(Course course, TEntity entity);//Added
void DeleteEmployee(Employee employe);//Modified
void DeleteCourse(Course course);//Added
}
Thx for your answer.
Hello Vince. Well if you want to add additional model to this project you should follow all the steps from the beginning of this article. So, first you should create a Course model class, then you create a public DbSet Coursees {get;set;} property in the current EmployeeContext class. Then you should start another migration, as we did, to create additional table in the database. After that, you don’t have to change IDataRepository interface because it is generic one. It accepts any model you send to it. What you need to do is to create another CourseManager class that inherits from the IDataRepository interface and implements it as well. Then to register it inside the IOC container as we did in the ConfigureServices method like this: services.AddScoped, CourseManager>(); object and use it in your actions as we did in the EmployeeController. That should solve all of your problems. As I said, just do the same thing as we did in this article.
And then you can create another controller, inject your IDataRepository