হ্যালো হাসিব! ডাটাবেস নিয়ে কাজ করার জন্য আজকের লেকচারটি মোস্ট ইম্পর্ট্যান্ট। এখানে EF Core-এর হার্ট বা মূল চালিকাশক্তি—DbContext এবং DbSet নিয়ে আলোচনা করা হয়েছে। যেহেতু তুমি Fedora Linux এবং VS Code-এ শিফট করেছো, তাই লেকচারের ভিজ্যুয়াল স্টুডিও কেন্দ্রিক পুরনো কিছু নিয়মের বদলে আমি তোমাকে সম্পূর্ণ আধুনিক এবং .NET CLI ভিত্তিক স্মার্ট গাইডলাইন দেবো।

প্রথমে রিভিশনের সুবিধার্থে পুরো লেকচারটির একটি কুইক সামারি দেখে নেওয়া যাক।

📝 Lecture Summary at a Glance

  • DbContext & DbSet: DbContext পুরো Database-কে রিপ্রেজেন্ট করে এবং এর ভেতরে থাকা প্রতিটি DbSet<T> এক একটি নির্দিষ্ট Table-কে নির্দেশ করে।
  • Data Annotations: ডাটাবেস টেবিলের Primary Key ঠিক করার জন্য [Key] এবং কলাম সাইজ অপটিমাইজ করে nvarchar(max) এড়ানোর জন্য [StringLength] অ্যাট্রিবিউট ব্যবহার করা হয়।
  • Nullable Types: C#-এ ডাটা টাইপের পাশে ? দিলে সেটি ডাটাবেসে Nullable কলাম হিসেবে তৈরি হয়। না দিলে সেটি Mandatory (NOT NULL) কলাম হয়।
  • Fluent API (OnModelCreating): মডেল ক্লাসের সাথে ডাটাবেস টেবিলের ম্যাপিং বা নাম সুনির্দিষ্ট করার জন্য OnModelCreating মেথড ওভাররাইড করে ModelBuilder ব্যবহার করা হয়।
  • Dependency Injection: Program.cs ফাইলে AddDbContext ব্যবহার করে ডাটাবেসকে সার্ভিস হিসেবে রেজিস্ট্রি করা হয়, যাতে সরাসরি Controller-এ না নিয়ে Service Layer-এ ইনজেক্ট করে Separation of Concerns ধরে রাখা যায়।

🧠 Comprehensive Breakdown & Deep Dive

১. DbContext এবং DbSet এর মূল ধারণা [Importance: 10/10]

  • The “Why”: তোমার C# কোড কীভাবে জানবে যে ডাটাবেসের নাম কী বা সেখানে কী কী টেবিল আছে? এই পুরো ডাটাবেস কানেকশন এবং কনফিগারেশনের ম্যানেজার হলো DbContext ক্লাস। আর ডাটাবেসের টেবিলগুলোর সাথে কোডের Model Class-এর ম্যাপিং ডিক্লেয়ার করার জন্য ব্যবহার করা হয় DbSet
  • কনভেনশন: মডেল ক্লাসের নাম সবসময় Singular (একবচন) হয়, যেমন: Country, Person। আর DbSet-এর নাম বা ডাটাবেস টেবিলের নাম Plural (বহুবচন) রাখা স্ট্যান্ডার্ড, যেমন: Countries, Persons

২. Model Configuration এবং Data Annotations [Importance: 9/10]

  • The “Why”: ডাটাবেস কলামের ডেটা টাইপ এবং সাইজ পারফেক্টলি অপটিমাইজ না করলে ডাটাবেসের সাইজ আননেসেসারি বেড়ে যাবে এবং পারফরম্যান্স ড্রপ করবে। যেমন C#-এর string টাইপ ডিফল্টভাবে SQL Server-এ nvarchar(max) (২ বিলিয়ন ক্যারেক্টার) হিসেবে তৈরি হয়, যা অত্যন্ত ব্যাড প্র্যাকটিস।

💻 C# 13/10 আপডেটেড মডেল ইমপ্লিমেন্টেশন:

using System.ComponentModel.DataAnnotations;
 
namespace Entities;
 
public class Country
{
    [Key] // এটি Primary Key নির্ধারণ করে
    public Guid CountryId { get; set; }
 
    [StringLength(50)] // nvarchar(max) এর বদলে nvarchar(50) তৈরি করবে
    public string? CountryName { get; set; } // '?' মানে এটি ডাটাবেসে NULL হতে পারবে
}
 
public class Person
{
    [Key]
    public Guid PersonId { get; set; }
 
    [Required] // এটি NOT NULL কলাম তৈরি করবে (C# 11+ এ required কিওয়ার্ডও ব্যবহার করা যায়)
    [StringLength(40)]
    public string PersonName { get; set; } = null!; // Non-nullable string initialization
 
    [StringLength(10)]
    public string? Gender { get; set; }
 
    [StringLength(200)]
    public string? Address { get; set; }
}
 

৩. Cross-Platform Package Installation (.NET CLI) [Importance: 8/10]

  • The “Why”: লেকচারে ভিজ্যুয়াল স্টুডিওর GUI এবং Package Manager Console (Install-Package) দেখানো হয়েছে। কিন্তু হাসিব, তুমি যেহেতু লিনাক্সে আছো, তোমার জন্য সবচেয়ে স্মার্ট এবং ইউনিভার্সাল উপায় হলো .NET CLI ব্যবহার করা।
  • লেকচারার বলেছেন এই প্যাকেজটি Entities প্রজেক্ট এবং মূল Web প্রজেক্ট—উভয় জায়গাতেই ইনস্টল করতে হবে।

💻 Linux Terminal / VS Code CLI Commands: তোমার টার্মিনালে প্রজেক্ট ডিরেক্টরিতে গিয়ে নিচের কমান্ডগুলো রান করবে:

# Entities প্রজেক্টের ফোল্ডারে গিয়ে:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
 
# মূল Web/API (CRUD Example) প্রজেক্টের ফোল্ডারে গিয়ে:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
 

৪. OnModelCreating এবং Fluent API দিয়ে Table Mapping [Importance: 8/10]

  • The “Why”: Data Annotations-এর চেয়ে OnModelCreating (Fluent API) ব্যবহার করা অনেক বেশি প্রফেশনাল এবং ক্লিন। এর মাধ্যমে মডেল ক্লাসের ভেতরে নোংরা কোড না লিখে এক জায়গা থেকেই টেবিলের নাম এবং স্ট্রাকচার কন্ট্রোল করা যায়।

💻 DbContext ইমপ্লিমেন্টেশন:

using Microsoft.EntityFrameworkCore;
 
namespace Entities;
 
public class PersonsDbContext : DbContext
{
    // ১. .NET 10/8 এর আধুনিক Primary Constructor বা সাধারণ কনস্ট্রাক্টর (নিচে .NET 10 সেকশনে আধুনিক রূপটি দেখাবো)
    public PersonsDbContext(DbContextOptions<PersonsDbContext> options) : base(options)
    {
    }
 
    // ২. DbSet ডিক্লেয়ারেশন
    public DbSet<Country> Countries { get; set; }
    public DbSet<Person> Persons { get; set; }
 
    // ৩. Fluent API এর মাধ্যমে টেবিল ম্যাপিং
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
 
        // সিউর হওয়া যে ডাটাবেস টেবিলের নাম 'countries' এবং 'persons' ই হবে
        modelBuilder.Entity<Country>().ToTable("countries");
        modelBuilder.Entity<Person>().ToTable("persons");
    }
}
 

VS Code Shortcut: মেথড ওভাররাইড করার সময় পুরোটা টাইপ করার দরকার নেই। মেথডের ভেতরে override লিখে একটি স্পেস দিলে VS Code-এর IntelliSense তোমাকে লিস্ট দেখাবে, সেখান থেকে OnModelCreating সিলেক্ট করলেই পুরো সিগনেচার অটো জেনারেট হয়ে যাবে।

৫. Program.cs-এ DbContext রেজিস্ট্রি করা (Dependency Injection) [Importance: 10/10]

  • The “Why”: লেকচারার একটি দারুণ পয়েন্ট বলেছেন—Clean Separation of Concerns। ডাটাবেসের কোড বা DbContext সরাসরি Controller-এ ব্যবহার করা উচিত নয়, এতে কোড টাইটলি কাপলড হয়ে যায় এবং টেস্ট করা অসম্ভব হয়ে পড়ে। তাই আমরা একে Program.cs-এ Dependency Injection (DI) কন্টেইনারে রেজিস্ট্রি করবো এবং সার্ভিস লেয়ারে ইনজেক্ট করবো।

💻 Program.cs ইমপ্লিমেন্টেশন:

using Entities;
using Microsoft.EntityFrameworkCore;
 
var builder = WebApplication.CreateBuilder(args);
 
// DbContext কে SQL Server প্রোভাইডার সহ সার্ভিস হিসেবে অ্যাড করা
builder.Services.AddDbContext<PersonsDbContext>(options =>
{
    // কানেকশন স্ট্রিংটি পরবর্তী লেকচারে শিখানো হবে, আপাতত এটি ডেমো
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
 
var app = builder.Build();
 

🚀 Modern .NET 10 Upgrades & Best Practices

.NET 10-এ DbContext-এর স্মার্ট পরিবর্তন (Primary Constructors)

.NET 8 এবং ১০-এ প্রজেক্টের কোড আরও ছোট করার জন্য Primary Constructors ব্যবহার করা যায়। আগের মতো লম্বা কনস্ট্রাক্টর না লিখে তুমি এক লাইনে DbContext ডিফাইন করতে পারো।

// ✅ .NET 10 Clean Approach (Using Primary Constructor)
public class PersonsDbContext(DbContextOptions<PersonsDbContext> options) : DbContext(options)
{
    public DbSet<Country> Countries { get; set; }
    public DbSet<Person> Persons { get; set; }
 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Country>().ToTable("Countries");
    }
}
 

💡 Best Practices for DbContext & DbSet

১. Avoid Required Attribute on Nullable Reference Types: সি-শার্পের নতুন ভার্সনগুলোতে string? দিলে সেটি অটোমেটিক ডাটাবেসে Nullable হয়। তাই ডাটা টাইপের ডিফাইন প্র্যাকটিস ক্লিয়ার রাখবে। Non-nullable string হলে = null!; বা required string ব্যবহার করবে। ২. Use Fluent API for Complex Configurations: টেবিলের নাম, রিলেশনশিপ (One-to-Many, Many-to-Many) এবং ইনডেক্সিং করার সময় মডেল ক্লাসে Data Annotation ভিড় না করে OnModelCreating-এর ভেতরেই সব কনফিগারেশন রাখবে। এতে ডোমেইন মডেল ক্লিন থাকে। ৩. Always Configure Timeouts and Retries: রিয়েল-ওয়ার্ল্ড অ্যাপ্লিকেশনে ডাটাবেস কানেকশন ড্রপ করতেই পারে। তাই UseSqlServer করার সময় রিট্রাই পলিসি যুক্ত করা বেস্ট প্র্যাকটিস:

options.UseSqlServer(connectionString, sqlOptions => 
{
    sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
 

পরের লেকচারে কানেকশন স্ট্রিং কনফিগারেশন নিয়ে আলোচনা করা হবে। তুমি রেডি হলে পরের ট্রান্সক্রিপ্টটি দিতে পারো!