হ্যালো হাসিব! এই লেকচারটি তোমার ব্যাকএন্ড ডেভেলপমেন্ট জার্নির অন্যতম গুরুত্বপূর্ণ একটি মাইলফলক। এতক্ষণ আমরা শুধু ডাটাবেস এবং টেবিল সেটআপ করেছি, কিন্তু এই লেকচারে দেখানো হয়েছে কীভাবে তোমার C# কোড (Service Layer) থেকে সেই ডাটাবেসে CRUD (Create, Read, Update, Delete) অপারেশন চালানো হয়।
পুরো লেকচারটিতে বেশ কিছু ক্রিটিক্যাল এরর (Errors) এবং আর্কিটেকচারাল কনসেপ্ট নিয়ে আলোচনা করা হয়েছে। চলো প্রথমে এর একটি কুইক সামারি দেখে নিই।
📝 Lecture Summary at a Glance
- DbContext Injection: In-Memory Collection (List) বাদ দিয়ে Service ক্লাসে
DbContextইনজেক্ট করা হয়েছে ডাটাবেস এক্সেস করার জন্য। - CRUD Operations: ডাটা রিড করার জন্য LINQ, ইনসার্ট করার জন্য
Add(), ডিলিট করার জন্যRemove()এবং সবশেষে ডাটাবেসে সেভ করার জন্যSaveChanges()ব্যবহার করা হয়েছে। - Entity State: EF Core ইন্টারনালি ডাটার স্টেট ট্র্যাক করে (
Added,Modified,Removed)।SaveChanges()কল করলে এই স্টেটের ওপর ভিত্তি করেই ইন্টারনালি SQL কুয়েরি জেনারেট হয়। - DI Lifetime Error: Scoped সার্ভিস (
DbContext) কখনো Singleton সার্ভিসের ভেতরে ইনজেক্ট করা যায় না। তাই সার্ভিসের লাইফটাইমAddSingletonথেকেAddScopedএ পরিবর্তন করা হয়েছে। - LINQ Projection Error: LINQ to Entities কুয়েরির ভেতরে নিজস্ব কোনো কাস্টম মেথড কল করা যায় না। আগে ডাটা
ToList()দিয়ে মেমোরিতে আনতে হয়, তারপর কাস্টম মেথড দিয়ে ম্যাপ করতে হয়।
🧠 Comprehensive Breakdown & Deep Dive
১. Injecting DbContext in Services [Importance: 9/10]
- The “Why”: Controller থেকে সরাসরি ডাটাবেসে কল করা ব্যাড প্র্যাকটিস। তাই আমরা Service লেয়ারে
DbContextইনজেক্ট করে ডাটাবেসের কাজগুলো সেখানে করি। - কীভাবে কাজ করে: ডিপেন্ডেন্সি ইনজেকশনের (Dependency Injection) মাধ্যমে
PersonsDbContextকে সার্ভিসের কনস্ট্রাক্টরে রিসিভ করে একটি প্রাইভেট ফিল্ডে (_db) রাখা হয়।
💻 Modern C# (Primary Constructor) Implementation: লেকচারে ট্র্যাডিশনাল কনস্ট্রাক্টর দেখানো হয়েছে। .NET 12/10-এ তুমি Primary Constructor ব্যবহার করে কোড আরও ক্লিন করতে পারো:
public class PersonsService(PersonsDbContext db, ICountriesService countriesService) : IPersonsService
{
private readonly PersonsDbContext _db = db;
private readonly ICountriesService _countriesService = countriesService;
// In-memory লিস্ট ডিলিট করে দেওয়া হয়েছে, এখন সব কাজ _db দিয়ে হবে।
}
২. Create, Update, Delete এবং SaveChanges() [Importance: 10/10]
- The “Why”: তুমি যখন
_db.Persons.Add(person)লেখো, তখন ডাটাবেসে সাথে সাথে ডাটা ইনসার্ট হয় না। এটি শুধু EF Core-এর মেমোরিতেEntityState.Addedহিসেবে মার্ক হয়ে থাকে।SaveChanges()মেথডটি মূলত এই ট্র্যাকার চেক করে ডাটাবেসে রিয়েল SQL (INSERT, UPDATE, DELETE) এক্সিকিউট করে।
💻 Code Implementation:
// 🟢 Create Operation
_db.Persons.Add(newPerson); // State: Added
_db.SaveChanges(); // Executes: INSERT INTO...
// 🔵 Update Operation
var existingPerson = _db.Persons.FirstOrDefault(p => p.PersonId == id);
existingPerson.PersonName = "Hasib"; // State automatically changes to: Modified
_db.SaveChanges(); // Executes: UPDATE...
// 🔴 Delete Operation
var personToDelete = _db.Persons.FirstOrDefault(p => p.PersonId == id);
_db.Persons.Remove(personToDelete); // State: Deleted
_db.SaveChanges(); // Executes: DELETE FROM...
৩. Dependency Injection Lifetime Conflict (AggregateException) [Importance: 10/10]
- The “Why”: প্রজেক্ট রান করার পর লেকচারার একটি এরর পেয়েছেন: “Cannot consume scoped service from a singleton service”.
- Reason:
DbContextবাই-ডিফল্ট একটি Scoped সার্ভিস (অর্থাৎ প্রতি HTTP রিকোয়েস্টে একবার তৈরি হয়)। কিন্তু আগে থেকেCountriesServiceকেProgram.cs-এ Singleton (পুরো অ্যাপ্লিকেশনের লাইফটাইমে একবার তৈরি হয়) হিসেবে রেজিস্টার করা ছিল। একটি দীর্ঘস্থায়ী (Singleton) অবজেক্টের ভেতরে স্বল্পস্থায়ী (Scoped) অবজেক্ট ইনজেক্ট করলে মেমোরি লিক এবং ডাটা করাপশন হয়। - Solution:
Program.csফাইলে গিয়েbuilder.Services.AddSingletonপরিবর্তন করেAddScopedকরে দিতে হবে।
৪. LINQ Translation Error (InvalidOperationException) [Importance: 10/10]
- The “Why”: ডাটা রিড করার সময় আরেকটি বড় এরর আসে: “The client projection contains a reference to a constant expression… ”।
- Reason: তুমি যখন ডাটাবেস থেকে ডাটা আনার জন্য LINQ লেখো (যেমন
.Select()), EF Core সেই LINQ কে SQL কুয়েরিতে কনভার্ট করে। কিন্তু তুমি যদিSelect-এর ভেতরে তোমার নিজের বানানো কোনো C# মেথড (যেমনConvertPersonToPersonResponse()) কল করো, EF Core তো আর জানে না এটাকে কীভাবে SQL সার্ভারের ভাষায় ট্রান্সলেট করতে হবে! - Solution: আগে
ToList()কল করে ডাটাবেস থেকে ডাটাগুলো RAM-এ (In-Memory) নিয়ে আসতে হবে, তারপর তুমি সেই ডাটার ওপর তোমার কাস্টম মেথড চালাতে পারবে।
// ❌ Error Code (EF Core doesn't know how to translate your custom method into SQL):
var response = _db.Persons.Select(p => ConvertPersonToPersonResponse(p)).ToList();
// ✅ Fixed Code (Fetch to memory first, then convert):
var personsFromDb = _db.Persons.ToList(); // Executes SELECT * FROM Persons
var response = personsFromDb.Select(p => ConvertPersonToPersonResponse(p)).ToList(); // Works perfectly in RAM
🚀 Best Practices & .NET 10 Updates
লেকচারার এখানে সব মেথড Synchronous (যেমন ToList, SaveChanges) ব্যবহার করেছেন, যা রিয়েল-ওয়ার্ল্ড বা প্রোডাকশন গ্রেড অ্যাপ্লিকেশনের জন্য একদমই ঠিক নয়। নিচে আমি আধুনিক এবং পারফরম্যান্ট উপায়গুলো দিয়ে দিচ্ছি:
১. Always Use Async/Await: ডাটাবেস কলগুলোতে সবসময় Asynchronous মেথড ব্যবহার করবে, এতে তোমার সার্ভারের থ্রেড ব্লক হবে না।
ToList()এর বদলেToListAsync()FirstOrDefault()এর বদলেFirstOrDefaultAsync()SaveChanges()এর বদলেSaveChangesAsync()
২. Modern Update & Delete (ExecuteUpdate & ExecuteDelete):
লেকচারে Update বা Delete করার জন্য আগে ডাটাবেস থেকে ডাটা রিড করে মেমোরিতে আনা হয়েছে, তারপর Remove করে SaveChanges করা হয়েছে। .NET 8/10-এ এত ঝামেলার দরকার নেই। তুমি এক লাইনেই সরাসরি ডাটাবেসে আপডেট বা ডিলিট কমান্ড পাঠাতে পারো, যা বহুগুণ ফাস্ট!
// ✅ .NET 8/10 Super Fast Delete (No need to read data first!)
await _db.Persons
.Where(p => p.PersonId == id)
.ExecuteDeleteAsync();
// ✅ .NET 8/10 Super Fast Update (No SaveChanges required!)
await _db.Persons
.Where(p => p.PersonId == id)
.ExecuteUpdateAsync(setters => setters.SetProperty(p => p.PersonName, "New Name"));
৩. Read-Only Queries এ AsNoTracking():
যখন তুমি শুধু ডাটা রিড করে ইউজারকে দেখাবে (যেমন GetAllPersons), তখন EF Core-এর ডাটা ট্র্যাক করার কোনো দরকার নেই। তাই কুয়েরির মাঝে .AsNoTracking() ব্যবহার করলে পারফরম্যান্স অনেক বেড়ে যায়।
var persons = await _db.Persons.AsNoTracking().ToListAsync();
এই লেকচারের মাধ্যমে তুমি সফলভাবে ইন-মেমোরি থেকে আসল SQL ডাটাবেসে শিফট করেছো। এর পরের টপিকে EF Core এর ইন্টারনাল কুয়েরি এক্সিকিউশন নিয়ে আলোচনা হবে। পরবর্তী ট্রান্সক্রিপ্ট দিতে পারো!