While being very efficient, Entity Framework (EF) & Entity Framework Core (EF Core) do not allow you to natively perform bulk operations, Hopefully, there is an open-source library that solves the problem It is known as EF Core Bulk extensions. This article shows how to use EF Core Bulk Extensions on a .Net project that relies on EF Core.

Problem with EF, EFCore while dealing with huge data

We should not directly insert 100k data into the database by using Entity Framework. It may take a few minutes to perform the entire task. EntityFramework has been criticized when it comes to performance. The efficient way of dealing with such a large amount of data under such conditions, it is common to go back using ADO.Net to accomplish the task. However, if you have used the Entity Framework in your project, the combination of ADO.Net and SQLBulkCopy will break the benefits of the EntityFramework as an ORM (Object Relation Mapping)

Compare Performance between Bulk Insert vs Add Range

It is said that we can insert large data over 20 times faster than a regular insert. See the comparison below
Source Code - Git Hub Repo
Project Setup
  • Create a Web API template with the latest .Net Core installed in your machine
Require packages - To perform the CRUD Operations using Code First approach.
Package to Perform EF Core Bulk Operations
Create a model and DbContext where we can perform the table creation in the SQL Database with the configuration setup.
Employee.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. namespace BulkOperations_EFCore.Models
  7. {
  8. public class Employee
  9. {
  10. [Key]
  11. public int Id{ get; set; }
  12. public string Name { get; set; }
  13. public string Designation { get; set; }
  14. public string City { get; set; }
  15. }
  16. }
AppDbContext.cs
  1. using Microsoft.EntityFrameworkCore;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. namespace BulkOperations_EFCore.Models
  7. {
  8. public class AppDbContext : DbContext
  9. {
  10. public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
  11. {
  12. }
  13. public DbSet<Employee> Employees { get; set; }
  14. }
  15. }
Set up the connection string in the appsettings.json file
appsettings.json
  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Information",
  5. "Microsoft": "Warning",
  6. "Microsoft.Hosting.Lifetime": "Information"
  7. }
  8. },
  9. "AllowedHosts": "*",
  10. "ConnectionStrings": {
  11. "myconn": "server=*Your Server Name*; database=bulkops;Trusted_Connection=True;"
  12. }
  13. }
Startup.cs
  1. using BulkOperations_EFCore.BusinessLogic;
  2. using BulkOperations_EFCore.Models;
  3. using Microsoft.AspNetCore.Builder;
  4. using Microsoft.AspNetCore.Hosting;
  5. using Microsoft.AspNetCore.HttpsPolicy;
  6. using Microsoft.AspNetCore.Mvc;
  7. using Microsoft.EntityFrameworkCore;
  8. using Microsoft.Extensions.Configuration;
  9. using Microsoft.Extensions.DependencyInjection;
  10. using Microsoft.Extensions.Hosting;
  11. using Microsoft.Extensions.Logging;
  12. using Microsoft.OpenApi.Models;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Threading.Tasks;
  17. namespace BulkOperations_EFCore
  18. {
  19. public class Startup
  20. {
  21. public Startup(IConfiguration configuration)
  22. {
  23. Configuration = configuration;
  24. }
  25. public IConfiguration Configuration { get; }
  26. // This method gets called by the runtime. Use this method to add services to the container.
  27. public void ConfigureServices(IServiceCollection services)
  28. {
  29. services.AddControllers();
  30. services.AddSwaggerGen(c =>
  31. {
  32. c.SwaggerDoc("v1", new OpenApiInfo { Title = "BulkOperations_EFCore", Version = "v1" });
  33. });
  34. #region Connection String
  35. services.AddDbContext<AppDbContext>(item => item.UseSqlServer(Configuration.GetConnectionString("myconn")));
  36. #endregion
  37. services.AddScoped<EmployeeService>();
  38. }
  39. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  40. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  41. {
  42. if (env.IsDevelopment())
  43. {
  44. app.UseDeveloperExceptionPage();
  45. app.UseSwagger();
  46. app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "BulkOperations_EFCore v1"));
  47. }
  48. app.UseHttpsRedirection();
  49. app.UseRouting();
  50. app.UseAuthorization();
  51. app.UseEndpoints(endpoints =>
  52. {
  53. endpoints.MapControllers();
  54. });
  55. }
  56. }
  57. }
Create a Class named as EmployeeService in which we can add all the Methods to perform the CRUD Operations using the EF Core and Bulk Operations and added a loop to perform (100k records) for bulk insert and bulk update and along with Bulk delete.
EmployeeService.cs
  1. using BulkOperations_EFCore.Models;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using EFCore.BulkExtensions;
  7. namespace BulkOperations_EFCore.BusinessLogic
  8. {
  9. public class EmployeeService
  10. {
  11. private readonly AppDbContext _appDbContext;
  12. private DateTime Start;
  13. private TimeSpan TimeSpan;
  14. //The "duration" variable contains Execution time when we doing the operations (Insert,Update,Delete)
  15. public EmployeeService(AppDbContext appDbContext)
  16. {
  17. _appDbContext = appDbContext;
  18. }
  19. #region Add Bulk Data
  20. public async Task<TimeSpan> AddBulkDataAsync()
  21. {
  22. List<Employee> employees = new(); // C# 9 Syntax.
  23. Start = DateTime.Now;
  24. for (int i = 0; i < 100000; i++)
  25. {
  26. employees.Add(new Employee()
  27. {
  28. Name = "Employee_" + i,
  29. Designation = "Designation_" + i,
  30. City = "City_" + i
  31. });
  32. }
  33. await _appDbContext.BulkInsertAsync(employees);
  34. TimeSpan = DateTime.Now - Start;
  35. return TimeSpan;
  36. }
  37. #endregion
  38. #region Update Bulk Data
  39. public async Task<TimeSpan> UpdateBulkDataAsync()
  40. {
  41. List<Employee> employees = new(); // C# 9 Syntax.
  42. Start = DateTime.Now;
  43. for (int i = 0; i < 100000; i++)
  44. {
  45. employees.Add(new Employee()
  46. {
  47. Id = (i + 1),
  48. Name = "Update Employee_" + i,
  49. Designation = "Update Designation_" + i,
  50. City = "Update City_" + i
  51. });
  52. }
  53. await _appDbContext.BulkUpdateAsync(employees);
  54. TimeSpan = DateTime.Now - Start;
  55. return TimeSpan;
  56. }
  57. #endregion
  58. #region Delete Bulk Data
  59. public async Task<TimeSpan> DeleteBulkDataAsync()
  60. {
  61. List<Employee> employees = new(); // C# 9 Syntax.
  62. Start = DateTime.Now;
  63. employees = _appDbContext.Employees.ToList();
  64. await _appDbContext.BulkDeleteAsync(employees);
  65. TimeSpan = DateTime.Now - Start;
  66. return TimeSpan;
  67. }
  68. #endregion
  69. }
  70. }
Let's create an individual endpoint for all the respective service methods inside the controller class.
BulkOperationsController.cs
  1. using BulkOperations_EFCore.BusinessLogic;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Threading.Tasks;
  8. namespace BulkOperations_EFCore.Controllers
  9. {
  10. [Route("api/[controller]")]
  11. [ApiController]
  12. public class BulkOperationsController : ControllerBase
  13. {
  14. private readonly EmployeeService _employeeService;
  15. public BulkOperationsController(EmployeeService employeeService)
  16. {
  17. _employeeService = employeeService;
  18. }
  19. [HttpPost(nameof(AddBulkData))]
  20. public async Task<IActionResult> AddBulkData()
  21. {
  22. var response = await _employeeService.AddBulkDataAsync();
  23. return Ok(response);
  24. }
  25. [HttpPut(nameof(UpdateBulkData))]
  26. public async Task<IActionResult> UpdateBulkData()
  27. {
  28. var response = await _employeeService.UpdateBulkDataAsync();
  29. return Ok(response);
  30. }
  31. [HttpDelete(nameof(DeleteBulkData))]
  32. public async Task<IActionResult> DeleteBulkData()
  33. {
  34. var response = await _employeeService.DeleteBulkDataAsync();
  35. return Ok(response);
  36. }
  37. }
  38. }
Testing the Endpoints
Test the API to check how much time is consumed to complete the operation. It hardly takes 4 Sec to insert all the 100k records.
Update - API (9 Sec - 100k records)
Delete - API (3 Sec - 100k records)
Query to fetch the data and count in SQL Server
After execution of Update API
Thanks for reading and please keep visiting and sharing with your community.
Happy Coding..!