<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shahriarKabir44</title>
    <description>The latest articles on DEV Community by shahriarKabir44 (@shahriarkabir44).</description>
    <link>https://dev.to/shahriarkabir44</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F549847%2Ff8f185db-6b2a-4f3b-8a9e-e79f5ae621c3.png</url>
      <title>DEV Community: shahriarKabir44</title>
      <link>https://dev.to/shahriarkabir44</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shahriarkabir44"/>
    <language>en</language>
    <item>
      <title>Optimizing ASP.NET + EF6 for Bulk Insertion and Massive Data Selection: A Practical Guide</title>
      <dc:creator>shahriarKabir44</dc:creator>
      <pubDate>Mon, 10 Aug 2026 20:12:17 +0000</pubDate>
      <link>https://dev.to/shahriarkabir44/optimizing-aspnet-ef6-for-bulk-insertion-and-massive-data-selection-a-practical-guide-3i5i</link>
      <guid>https://dev.to/shahriarkabir44/optimizing-aspnet-ef6-for-bulk-insertion-and-massive-data-selection-a-practical-guide-3i5i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A few days ago, one of our clients' accounting officials uploaded an Excel file with student bills and payments — a routine task, done every semester. Except this time, it wasn't routine. The validation API took almost 2 minutes just to process 200 rows, checking each one for duplicates against a database holding over 4 million records. Once validated, hitting "confirm" kicked off the real nightmare: 10+ minutes of waiting, staring at a loading spinner, hoping the connection wouldn't time out before the save finished.&lt;/p&gt;

&lt;p&gt;Multiply that by every accounting officer, every semester, every batch of a few thousand students — and you've got a feature that was technically working, but practically unusable at scale.&lt;/p&gt;

&lt;p&gt;So I decided to dig in. What I found wasn't one bug — it was a stack of small, very common EF6 mistakes, each one quietly compounding the next: row-by-row duplicate checks hitting the database individually, &lt;code&gt;SaveChanges()&lt;/code&gt; called inside a loop, and query patterns that scaled linearly (or worse) with row count instead of staying flat.&lt;/p&gt;

&lt;p&gt;After rewriting the pipeline, the same validation that took 2 minutes for 200 rows now handles &lt;strong&gt;5,000 rows in 20 seconds&lt;/strong&gt;. Saving, which used to take 10+ minutes, now finishes in &lt;strong&gt;1 minute 20 seconds&lt;/strong&gt; — for a batch 25x larger than before.&lt;/p&gt;

&lt;p&gt;This article walks through exactly what was slow, why it was slow, and the specific EF6 techniques — bulk insertion, batched lookups, and smarter querying for massive data selection — that got it there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: What We Are Trying to Achieve
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Context:
&lt;/h3&gt;

&lt;p&gt;An accounting officer uploads an excel file containing the students' bills and payments. One API accepts the file and validates it and check duplicates and returns a summary (Slow). Upon confirmation, another API inserts all the data from the uploaded excel file to the database (Suuuuuuuuuuper Slow).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Is It Harder than a typical CRUD API:
&lt;/h3&gt;

&lt;p&gt;As an institute grows, its data volume grows exponentially and reaches a point when normal database operations become expensive both in terms of memory and time consumption. Therefore, any sort of database operations that involves massive tables require extra care and optimization technique.&lt;/p&gt;

&lt;h2&gt;
  
  
  In This Article, We Will...
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Learn some tricks to make the data loading lighter and faster.&lt;/li&gt;
&lt;li&gt; Learn what to avoid.&lt;/li&gt;
&lt;li&gt; Learn how to optimize bulk insertions.&lt;/li&gt;
&lt;li&gt; Learn about some cool data structures.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tricks to Optimize Data Loading:
&lt;/h2&gt;

&lt;p&gt;This case is very much prevalent in massive tables that have many related tables via foreign keys, and you want to show the related data to the user in a report. For example, in a university, hundreds of courses are offered in each semester. Each offered course is related to faculties, students, curriculums etc. The offered class table might not be that big but if I want to show all the related data to the user, then it becomes a tough job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Approach to This Case:
&lt;/h3&gt;

&lt;p&gt;A very naive approach to develop this report would be to use the &lt;code&gt;Include()&lt;/code&gt; method to load all the related data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OfferedClass&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Curriculum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassRegisteredByStudent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassTakenByFaculty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pitfall of This Approach
&lt;/h3&gt;

&lt;p&gt;While using &lt;code&gt;Include()&lt;/code&gt;function makes the developer's life easier in the short term, it doesn't take much time to turn into a nightmare!!&lt;/p&gt;

&lt;h4&gt;
  
  
  Why?
&lt;/h4&gt;

&lt;p&gt;Because &lt;code&gt;Include()&lt;/code&gt; returns a Cartesian product (or Cross Join) for the tables. Suppose there are 10 offered class and 10 curriculums. EF doesn't fetch 10+10 rows for OfferedClass and Curriculum. It rather returns 10x10=100 rows. And it gets multiplied the more tables you include!&lt;/p&gt;

&lt;h3&gt;
  
  
  What Should We Do?
&lt;/h3&gt;

&lt;p&gt;Simple! We avoid using that method! Now let's discuss about the optimization, shall we?&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization 1: Avoid using &lt;code&gt;Include&lt;/code&gt; method and use the &lt;code&gt;Contains&lt;/code&gt; method instead.
&lt;/h2&gt;

&lt;p&gt;Let me explain. We don't need to be a genius to understand that the number of rows in a child table is almost always bigger than its parent table. That means, in our case the Curriculum table is a parent of the OfferedClass table. So, it's very likely that we are loading the same row from the Curriculum table by using the &lt;code&gt;Include&lt;/code&gt; method because there could be many OfferedClass from the same Curriculum. So, what should we do? The idea is to make a list of the unique primary keys of the Curriculum table. Then load the Curriculums where the IDs of each entity belongs to the list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;curriculumIdList&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;offeredClassList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurriculumId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Distinct&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;curriculumList&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Curriculum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;curriculumIdList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, EF runs a magic called &lt;strong&gt;relationship fix-up&lt;/strong&gt;. That is, against the &lt;em&gt;same context instance&lt;/em&gt;, EF checks each newly-loaded entity's foreign keys against entities already in that identity map. If a &lt;code&gt;OfferedClass.CurriculumId&lt;/code&gt; matches a &lt;code&gt;Curriculum.Id&lt;/code&gt; that's already tracked, EF wires up both sides: &lt;code&gt;OfferedClass.Curriculum&lt;/code&gt; gets set, and &lt;code&gt;Curriculum.OfferedClasses&lt;/code&gt; gets that OfferedClass added to it.&lt;/p&gt;

&lt;h4&gt;
  
  
  ⚠️ Warning:
&lt;/h4&gt;

&lt;p&gt;Two things to note here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Both data must be loaded using the same db context.&lt;/li&gt;
&lt;li&gt; Must turn off Lazy Loading. Otherwise, EF might load redundant data or duplicate data while insertion/update operations. &lt;code&gt;dbContext.Configuration.LazyLoadingEnabled = false;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimization 2: Avoid loading data inside any loop.
&lt;/h2&gt;

&lt;p&gt;Let's stick to our previous example. Suppose I also want to show the user how many students have registered to that offered class, how many classes each faculty has taken of each offered class etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Naive Approach (Bad)
&lt;/h3&gt;

&lt;p&gt;In such scenario, it's very much intuitive to iterate over the list of classes and for each class, we load the necessary data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is it bad?
&lt;/h3&gt;

&lt;p&gt;Well, simply put, database lives in storage devices and they are slow. Or at least not as fast as RAM. And each time we query to the database, a connection must be made, the SQL server reads the query, prepares the data, sends it back, entity framework reads and deserializes it. Multiply the time taken in the whole process by 1000 and the impact becomes vivid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Load All The Data in A List/Iterable and Then Use it.
&lt;/h3&gt;

&lt;p&gt;Instead of loading related data of each row one by one, we load them all at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;classIdList&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;offeredClassList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;studentRegistration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StudentRegistration&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;classIdList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RegistrationCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;offeredClass&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;offeredClassList&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;offeredClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegistrationCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;studentRegistration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;==&lt;/span&gt;&lt;span class="n"&gt;offeredClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="n"&gt;RegistrationCount&lt;/span&gt;&lt;span class="p"&gt;??&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait! We can do better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization 3: Use Cool Data Structures!
&lt;/h2&gt;

&lt;p&gt;Back in my University days when I was learning competitive programming, I learned about STL in C++ that provided some fancy data structures. Two of them were, Map and Set. In C#, we have Dictionary and HashSet. These bad boys help data lookups lightning fast! You see, looking up on an array/list is expensive. As much as O(N) expensive. So, clearly, looking up on a list inside a loop makes the algorithm O(N&lt;sup&gt;2&lt;/sup&gt;). We can use these data structures to improve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dictionaries instead of &lt;code&gt;FirstOrDefault&lt;/code&gt; or &lt;code&gt;Where&lt;/code&gt; Method
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; in .NET is a hash table. On lookup, it computes &lt;code&gt;key.GetHashCode()&lt;/code&gt;, uses that to jump straight to the right bucket, then does an equality check within that (usually tiny) bucket. No scanning the whole collection — that's what makes it constant time regardless of how many items are in the dictionary. Dictionary lookup complexity is O(1) on average case. 🤯&lt;/p&gt;

&lt;h4&gt;
  
  
  Let's Improve Our Last Code Snippet
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;classIdList&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;offeredClassList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;studentRegistration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StudentRegistration&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;classIdList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RegistrationCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegistrationCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;offeredClass&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;offeredClassList&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;offeredClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegistrationCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;studentRegistration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offeredClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea is to identify the columns we need filter on and create a key by concatenating the columns into a string. And this approach is not only limited to finding one element. You can use this approach to find a list that satisfies multiple criteria. All you have to do is to group the list by the filtering columns and convert it to a dictionary.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;HashSet&lt;/code&gt; for Checking Whether a Data Exists
&lt;/h3&gt;

&lt;p&gt;Let's head back to our first example about our accountant. Our code has to check for duplicate transactions in both excel and database. Let's consider the database part. A payment is considered duplicate if another payment with the same studentId, Date and Amount Exist. If any row from the excel data is found to be duplicate, we must mark it and highlight it in the UI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Naive Approach:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transactionsFromDb&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//load transactions from db (only of the students in the excel)&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;excelData&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionsFromDb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StudentId&lt;/span&gt;&lt;span class="p"&gt;==&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StudentId&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;==&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDuplicate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Smarter Approach Using HashSet
&lt;/h4&gt;

&lt;p&gt;The idea is to look for the columns that are to be matched and create a key by concatenating them in a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transactionsFromDb&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//load transactions from db (only of the students in the excel)&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transactionSet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transactionsFromDb&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StudentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"F2"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToHashSet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;excelData&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StudentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"F2"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactionSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDuplicate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Optimization 4: Select Only the Necessary Columns.
&lt;/h2&gt;

&lt;p&gt;Suppose the &lt;code&gt;User&lt;/code&gt; table has 100 columns. Some columns even have long texts. But on report, we only need to show a user's full name and the username. In that case, we only need to select the two columns from the database and not the whole table.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why?
&lt;/h4&gt;

&lt;p&gt;Selecting more columns means more RAM consumption, more CPU cycles to process the data. A combination that we certainly want to avoid, right? Of course, this means we need to declare many custom classes for very small use-cases. But when performance is not optional, we must not cut corners in coding. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;offeredClassIdList&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;offeredClassList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToHashSet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;facultyList&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;offeredClassIdList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UserShortInfo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Now Let's Talk About Bulk Insertion:
&lt;/h2&gt;

&lt;p&gt;Bulk insertion is a feature where lightning-fast speed is expected. And it comes with many caveats. It is very much different from inserting a single row. Few things that slow down the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Entity validations in the database layer. Such as data types, data consistency etc.&lt;/li&gt;
&lt;li&gt; Database transaction isolation level.&lt;/li&gt;
&lt;li&gt; Attempting to insert too much data in a single transaction.&lt;/li&gt;
&lt;li&gt; Saving each change one by one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's fix them:&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix-1: Disable Entity Framework's Safety Nets on save.
&lt;/h3&gt;

&lt;p&gt;Whenever you're trying to save a data, Entity framework tries to validate it. Which is of course an expensive operation for bulk insertions. We should create a solid custom validator that validates each property, checks nullability, data size, relationships etc. And we need to turn off the dbcontext's Entity Validator. &lt;code&gt;dbContext.Configuration.ValidateOnSaveEnabled = false;&lt;/code&gt; Now, the data validation is completely on your hand. Make no mistakes! We also need to disable Proxy Creation and Lazy loading to prevent loading unnecessary data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;  &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LazyLoadingEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProxyCreationEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix-2: Choosing the Proper Transaction Isolation Level.
&lt;/h3&gt;

&lt;p&gt;Putting too strict isolation level can make the db operation slow. It may even lock the entire table and put the user in a deadlock situation. Also in most cases, we need dirty-reads to get the ID of the inserted row. I personally prefer the &lt;code&gt;ReadUncommitted&lt;/code&gt; isolation level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix-3: Insertion by Chunk Instead of All at Once.
&lt;/h3&gt;

&lt;p&gt;Having too much uncommitted data consumes a lot of memory, rendering the process slow. Therefore, we need to insert data in multiple chunks and commit them.&lt;/p&gt;

&lt;h4&gt;
  
  
  ❌Bad Approach
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsolationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadUncommitted&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="c1"&gt;//.... validation and saving logic &lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ✅Better Approach (Slightly)
&lt;/h4&gt;

&lt;p&gt;The idea is to choose the size of a chunk. Calculate the number of chunks. Then select each chunk and insert. For each chunk, we need to re-initialize the dbContext and begin new transactions to free up the memory. The chunk size may depend on the use-case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chunkCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ceiling&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;chunkCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;++){&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DbContext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValidateOnSaveEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LazyLoadingEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProxyCreationEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsolationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadUncommitted&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="c1"&gt;//.... validation and saving logic &lt;/span&gt;
                &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix-4: Save Many Data at Once Instead of One by One
&lt;/h3&gt;

&lt;p&gt;Saving each data individually becomes really expensive at a large scale because it involves the database service over and over again. We should save all the data at once. We can store the items in a list. Then add the list into the database and save changes. This is much faster. Let's fix our previous code, shall we?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chunkCount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ceiling&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;chunkCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;++){&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;insertedPayments&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Payment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;itemList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DbContext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValidateOnSaveEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LazyLoadingEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProxyCreationEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsolationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadUncommitted&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="c1"&gt;//.... validation and saving logic &lt;/span&gt;
                &lt;span class="n"&gt;insertedPayments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;insertedPayments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;None of these fixes were exotic. No new library, no infrastructure upgrade, no throwing more hardware at the problem — just paying attention to how EF6 actually talks to the database and removing the patterns that quietly turn a fast query into a slow one at scale.&lt;/p&gt;

&lt;p&gt;The results speak for themselves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Validation:&lt;/strong&gt; ~1.7 rows/sec → ~250 rows/sec (&lt;strong&gt;~150x&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Saving:&lt;/strong&gt; ~0.3 rows/sec → ~62 rows/sec (&lt;strong&gt;~190x&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What used to be a 2-minute wait for 200 rows and a white-knuckle 10-minute save is now a validation pass on 5,000 rows in 20 seconds and a save in 1 minute 20 seconds. Accounting officials went from dreading this upload to not thinking about it at all — which, honestly, is the best compliment a backend feature can get.&lt;/p&gt;

&lt;p&gt;If you're dealing with something similar, here's the checklist to walk away with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Avoid using &lt;code&gt;Include&lt;/code&gt; as much as possible.&lt;/li&gt;
&lt;li&gt; Preload all the data instead of loading them inside a loop.&lt;/li&gt;
&lt;li&gt; Use dictionary and hashset to boost data lookups.&lt;/li&gt;
&lt;li&gt; Select only the necessary data.&lt;/li&gt;
&lt;li&gt; Turn off EF's safety nets in bulk insertions.&lt;/li&gt;
&lt;li&gt; Choose the proper isolation level.&lt;/li&gt;
&lt;li&gt; Don't try to insert too much data at once.&lt;/li&gt;
&lt;li&gt; Don't try to save each data individually. Rather save them in chunks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you've come this far, thank you so much for your time! I'd genuinely love to hear how you've handled EF6 performance at scale — drop a comment if you've got a war story or a technique I didn't cover.&lt;/p&gt;

&lt;h1&gt;
  
  
  Thank you!
&lt;/h1&gt;

</description>
      <category>csharp</category>
      <category>database</category>
      <category>dotnet</category>
      <category>performance</category>
    </item>
    <item>
      <title>Send Push Notifications Using Vanila JS! (Example with ExpressJS)</title>
      <dc:creator>shahriarKabir44</dc:creator>
      <pubDate>Tue, 20 Jan 2026 19:58:24 +0000</pubDate>
      <link>https://dev.to/shahriarkabir44/send-push-notifications-using-vanila-js-example-with-expressjs-36jp</link>
      <guid>https://dev.to/shahriarkabir44/send-push-notifications-using-vanila-js-example-with-expressjs-36jp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Making a lucrative UI or smooth UX are no longer enough to keep your clients attracted to you now-a-days! You need more. You need User Engagement. Specially if you're selling something online, an e-commerce perhaps. And you want to inform your users about a big sale that's coming on a specific festival. How? You might think that if you had an app, you might send your users some push notifications. Which is true but not really feasible in many cases specially when you're new to business. and then you wonder..&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; What if a web application could send push notificatons? 🤔
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Well lucky you because you're at the right spot!&lt;/p&gt;
&lt;h1&gt;
  
  
  In this article we'll...
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Do some basics.&lt;/li&gt;
&lt;li&gt;Create a simple web app with frontend and backend.&lt;/li&gt;
&lt;li&gt;Dive a bit deep on how it works.&lt;/li&gt;
&lt;li&gt;Some FAQs 🤓&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, let's go!&lt;/p&gt;
&lt;h2&gt;
  
  
  Environment Setup.
&lt;/h2&gt;

&lt;p&gt;Fire up your vscode in a folder and create a js file. This will be the server part from where we'll send the notifications. Now create a new project using npm with the command&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will create a package.json file for you.&lt;br&gt;
we need to install 2 npm packages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;b&gt; dotenv &lt;/b&gt; To store private and public VAPID key. (More on these later)&lt;/li&gt;
&lt;li&gt;
&lt;b&gt; web-push &lt;/b&gt; To actually send the notifications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Install them with the &lt;code&gt;npm install&lt;/code&gt; command.&lt;/p&gt;
&lt;h3&gt;
  
  
  The plan (Bird's Eye View):
&lt;/h3&gt;

&lt;p&gt;There are two major players in this game. &lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener noreferrer"&gt;ServiceWorkers&lt;/a&gt; and Web-Push&lt;br&gt;
At first, we need to create to two keys which are called VAPID (Voluntary Application Server Identification) keys one is public and another is private (Asymmetric encryption/decryption but let's not dig deeper here).&lt;br&gt;
The idea is that we register a serviceworker and use the subscription object to create a &lt;code&gt;PushSubscription&lt;/code&gt; object using the public VAPID key.&lt;br&gt;
We then send this object to the backend with a POST request. The server stores it. And in any event, the server uses the client's subscription object to send the notification.&lt;br&gt;
In short...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykuuhcheobrini1qguqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykuuhcheobrini1qguqs.png" alt="Data Flow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Enough with the chit chat!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhczdsni51mpst5gafe1j.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhczdsni51mpst5gafe1j.gif" alt="Let's Move!"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Structure:
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root/
├── client/
│   ├── index.html
│   ├── index.js
│   └── worker.js 
├── app.js
├── .env
├── package.json
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The Frontend:
&lt;/h2&gt;
&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;We'll keep it as simple as possible. We're only interested on getting a push notification on a button click. No fancy design, no CSS. Just a single button in HTML.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;h3&gt;
  
  
  ServiceWorker
&lt;/h3&gt;

&lt;p&gt;We'll limit our serviceworker's role to just receiving push notifications and show it.&lt;br&gt;
A notification needs to have three properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Icon&lt;/li&gt;
&lt;li&gt;Title&lt;/li&gt;
&lt;li&gt;Body
So, we'll design our payload object keeping this in mind. We'll use a fixed notification icon in this demonstration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;br&gt;
Nicely done!
&lt;h2&gt;
  
  
  The backend
&lt;/h2&gt;

&lt;p&gt;Let's do the bare minimum in our backend part in our &lt;code&gt;app.js&lt;/code&gt; file. &lt;br&gt;
Such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Including the necessary packages.&lt;/li&gt;
&lt;li&gt;Configuring the port.&lt;/li&gt;
&lt;li&gt;Pointing the static files directory.&lt;/li&gt;
&lt;li&gt;Configuring the environment variable. (We'll store our keys in the .env file btw)&lt;/li&gt;
&lt;li&gt;And an API endpoint to receive and store the client's info.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;




&lt;p&gt;Great! now the crucial part! &lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the VAPID keys
&lt;/h2&gt;

&lt;p&gt;Run the following command on your terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx web-push generate-vapid-keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You'll get an output like this..&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=======================================

Public Key:
&amp;lt;Public Key&amp;gt;

Private Key:
&amp;lt;Private Key&amp;gt;

=======================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Copy the keys and store them in your &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
Now, we register the ServiceWorker. &lt;br&gt;
Here's what we'd do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register the ServiceWorker (worker.js in our case)&lt;/li&gt;
&lt;li&gt;Create Push Subscription.
 2.1. Convert the public VAPID key to Uint8 Array.
 2.2. Create the PushSubscription object&lt;/li&gt;
&lt;li&gt;Post it to the backend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Like so..&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;p&gt;I'm sure that you're wondering what's happening in the following code:&lt;/p&gt;

&lt;pre&gt; function convertToUnit8Array(base64str) {
    const padding = '='.repeat((4 - (base64str.length % 4)) % 4)
    const base64 = (base64str + padding).replace(/\-/g, '+').replace(/_/g, '/')
    const rawData = atob(base64)
    var outputArray = new Uint8Array(rawData.length)
    for (let n = 0; n &amp;lt; rawData.length; n++) {
        outputArray[n] = rawData.charCodeAt(n)
    }
    return outputArray
}
&lt;/pre&gt;

&lt;p&gt;What it does is basically &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pad the string with '=' character to make its length divisible by 4. &lt;/li&gt;
&lt;li&gt;Convert Base64URL → normal Base64&lt;/li&gt;
&lt;li&gt;Decode Base64 → raw binary string
Basically, converting the human readable public key to machine readable byte array.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now let's update our server code, shall we?&lt;/p&gt;

&lt;p&gt;We'll configureour webpush with our public and private VAPID keys and an email address. For now, we're not providing any valid email. &lt;br&gt;
 We'll also implement a get API called &lt;code&gt;/sendNofication&lt;/code&gt; to send a notification to the client.&lt;br&gt;
 Here's the updated code:&lt;br&gt;
 

&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;br&gt;
Great! We're now ready!&lt;br&gt;
Fire up your server with the following command:

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And open your browser on the following URL:&lt;br&gt;
&lt;a href="http://localhost:4000/" rel="noopener noreferrer"&gt;localhost:4000&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And follow me!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43u0dun6ijs4j7q71jv2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43u0dun6ijs4j7q71jv2.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BOOM! We've implemented push notifications!&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6f2wihby01u8xw7hxgk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6f2wihby01u8xw7hxgk.gif" alt="enter image description here"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is it scalable?&lt;/strong&gt; &lt;br&gt;
     HELL YEAH 💪 It's the push notification services that does the main heavy lifting. Not you! (E.g. Google, Firefox, Apple etc depending on the browser). If you inspect the PushSubscription object, you'll find a property called &lt;code&gt;endpoint&lt;/code&gt;. When your server wants to send you a push notification, it basically becomes a REST API client and calls API mentioned in the endpoint property.&lt;br&gt;
&lt;strong&gt;Do you have to manage any states?&lt;/strong&gt; &lt;br&gt;
    You might think that since it's real-time, you'll have to store some state in the server like WebSocket. Luckily that's not the case. You only need to store the user's subscription data in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;So, we've implemented push notifications using vanilla js! Please let me know how I can improve this article. &lt;br&gt;
Here's the entire codebase btw if you're interested:&lt;br&gt;
&lt;a href="https://github.com/shahriarKabir44/push_notification_using_nodejs" rel="noopener noreferrer"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>serviceworkers</category>
      <category>pushnotifications</category>
    </item>
    <item>
      <title>Browser Dev Tools for Mobile!</title>
      <dc:creator>shahriarKabir44</dc:creator>
      <pubDate>Mon, 24 Nov 2025 17:46:33 +0000</pubDate>
      <link>https://dev.to/shahriarkabir44/browser-dev-tools-for-mobile-3nc8</link>
      <guid>https://dev.to/shahriarkabir44/browser-dev-tools-for-mobile-3nc8</guid>
      <description>&lt;h1&gt;
  
  
  Browser Dev Tools for Mobile!
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Modern day web browsers provide an important tool that we, web devs love and live for. The DevTools. We use it for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugging the JavaScript code on the client side.&lt;/li&gt;
&lt;li&gt;Test and tweak the styles.&lt;/li&gt;
&lt;li&gt;View the webpage in 3D to understand z-index.&lt;/li&gt;
&lt;li&gt;Intercepting HTTP requests.&lt;/li&gt;
&lt;li&gt;View the webpage on different devices' screen dimensions.
Basically, it's the Swiss Army Knife that makes our lives easier. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BUT!!
&lt;/h2&gt;

&lt;p&gt;How about doing the same stuff on the mobile devices? What if there's a bug that's showing up in only the mobile devices? Well, I personally have run into such cases several times and there's honestly no mobile App that's going to save our bum or at least make things easy. Well good news for us because I've found an approach that honestly blew my mind! &lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I'm using my Android Smartphone as I write this article. And we'll be using Chrome browser. This approach has a tiny prerequisite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to have developer options enabled on your smartphone before you proceed.&lt;/li&gt;
&lt;li&gt;Both your PC and your smartphone should be under the same network.&lt;/li&gt;
&lt;li&gt;We'll be using the Chrome Browser for this article. This means that you'll be needing Chrome both your PC and your smartphone.
That's it!
Now let's go step by step, shall we?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1:
&lt;/h3&gt;

&lt;p&gt;Connect your smartphone with your computer with a USB cable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2:
&lt;/h3&gt;

&lt;p&gt;Open Chrome browser on your PC and type the following URL on the URL bar:&lt;br&gt;
 &lt;code&gt;chrome://inspect/#devices&lt;/code&gt;&lt;br&gt;
 And you'll get a view like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwa3zwllsd0o7nde63iz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwa3zwllsd0o7nde63iz8.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3:
&lt;/h3&gt;

&lt;p&gt;Now, once you connect your phone with your PC, you'll see the following message on your notification tray. Click on it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt1q2pp0lq3dskmem8y1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt1q2pp0lq3dskmem8y1.jpg" alt=" " width="720" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4:
&lt;/h3&gt;

&lt;p&gt;Search for Wireless Debugging. Then allow it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgt6rsvebpgwlw5mivtdt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgt6rsvebpgwlw5mivtdt.jpg" alt=" " width="720" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5:
&lt;/h3&gt;

&lt;p&gt;Now open your chrome browser from your phone and open any website. Then look at your PC's chrome browser and you'll find all the tabs opened on your phone. Like so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8r93w8eas3spxa35105n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8r93w8eas3spxa35105n.png" alt=" " width="800" height="455"&gt;&lt;/a&gt;&lt;br&gt;
As you can see, I have opened W3School's JavaScript Tutorial article's page.&lt;br&gt;
Now go ahead and hit &lt;code&gt;inspect&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe84apsim44609b9xwoj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe84apsim44609b9xwoj7.png" alt=" " width="800" height="440"&gt;&lt;/a&gt;&lt;br&gt;
Voila!! 🥳🥳 You've got the DevTools to inspect your website from your mobile! What's even crazier is that you can also interact with the website form your PC and do whatever you want with the DevTool!!&lt;/p&gt;

&lt;p&gt;And if you have come this far, thank you so much! Please let me know if I missed anything! Peace ✌ &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devtool</category>
      <category>browser</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
