হ্যালো হাসিব! আমরা এই লেকচারটিতে PersonsRepository এর ইমপ্লিমেন্টেশন শেষ করেছি।
আগের লেকচারটিতে আমরা CountriesRepository বানিয়েছিলাম যা তুলনামূলক সহজ ছিল। কিন্তু PersonsRepository-তে কিছু অ্যাডভান্সড EF Core কুয়েরি, যেমন— ডাইনামিক প্রেডিকেট (Predicate) দিয়ে ফিল্টারিং করা এবং অবজেক্ট আপডেট করার কনসেপ্ট দেখানো হয়েছে।
চলো লেকচারটির একটি কুইক সামারি এবং বিস্তারিত পোস্টমর্টেম করে ফেলি।
📝 Lecture Summary at a Glance
- Class Creation:
Repositoriesপ্রজেক্টেPersonsRepositoryক্লাস তৈরি করেIPersonsRepositoryইন্টারফেস ইমপ্লিমেন্ট করা হয়েছে। - Dependency Injection: আগের মতোই
ApplicationDbContextকনস্ট্রাক্টরের মাধ্যমে ইনজেক্ট করা হয়েছে। - Delete Operation:
.RemoveRange()(বাRemove()) ব্যবহার করে ডাটা রিমুভ করা হয়েছে এবং.SaveChangesAsync()থেকে পাওয়া রো-কাউন্টের (Row count) ওপর ভিত্তি করেtrue/falseরিটার্ন করা হয়েছে। - Dynamic Filtering:
GetFilteredPersonsমেথডেExpression<Func<Person, bool>> predicateরিসিভ করে সরাসরি.Where(predicate)এ বসিয়ে দেওয়া হয়েছে, যা কুয়েরিকে চরম মাত্রায় ডায়নামিক করে তোলে। - Update Operation: আপডেট করার জন্য প্রথমে ডাটাবেস থেকে এক্সিস্টিং অবজেক্ট ফেচ করা হয়েছে, তারপর তার প্রপার্টিগুলো নতুন ডাটা দিয়ে ম্যানুয়ালি ওভাররাইট করে
.SaveChangesAsync()কল করা হয়েছে।
🧠 Comprehensive Breakdown & Deep Dive
১. Dynamic Filtering (The Power of Expressions) [Importance: 10/10]
- The “Why”: এটি এই লেকচারের সবচেয়ে চমৎকার পয়েন্ট! সাধারণত আমরা ডাটাবেস থেকে ডাটা ফিল্টার করার জন্য অনেকগুলো মেথড লিখি (যেমন
GetByEmail,GetByGender,GetByName)। কিন্তুExpression<Func<T, bool>>ব্যবহার করলে সার্ভিস লেয়ার যে শর্ত (Condition) দেবে, রিপোজিটরি ঠিক সেই শর্ত অনুযায়ীই ডাটাবেসেWHEREকুয়েরি ফায়ার করবে।
💻 Code Implementation (PersonsRepository.cs):
// প্যারামিটারে একটি ল্যাম্বডা এক্সপ্রেশন (Predicate) রিসিভ করা হচ্ছে
public async Task<List<Person>> GetFilteredPersons(Expression<Func<Person, bool>> predicate)
{
// Include দিয়ে Navigation Property (Country) আনা হলো
// এরপর Where ক্লজে সরাসরি সার্ভিসের পাঠানো এক্সপ্রেশন বসিয়ে দেওয়া হলো
return await _db.Persons
.Include(temp => temp.Country)
.Where(predicate)
.ToListAsync();
}
(এর ফলে ডাটাবেসে শুধু ফিল্টার করা ডাটাটুকুই লোড হবে, পুরো টেবিল মেমোরিতে আসবে না!)
২. Delete Operation & SaveChanges Output [Importance: 8/10]
- The “Why”: ডিলিট অপারেশন সাকসেসফুল হলো কিনা, তা চেক করার জন্য
SaveChangesএর রিটার্ন ভ্যালু ব্যবহার করাটা খুব স্মার্ট একটি হ্যাক।
💻 Code Implementation:
public async Task<bool> DeletePersonByPersonId(Guid personId)
{
// ১. ডিলিট করার জন্য এক্সিস্টিং ডাটা সিলেক্ট করা
_db.Persons.RemoveRange(_db.Persons.Where(temp => temp.PersonId == personId));
// ২. SaveChangesAsync কয়টি Row অ্যাফেক্ট করেছে তা রিটার্ন করে
int rowsDeleted = await _db.SaveChangesAsync();
// ৩. যদি ০ এর বেশি হয়, তার মানে ডিলিট সফল হয়েছে
return rowsDeleted > 0;
}
৩. Update Operation (The Manual Way) [Importance: 9/10]
- The “Why”: EF Core-এ আপডেট করার সবচেয়ে সেইফ উপায় হলো—প্রথমে ডাটাবেস থেকে রিয়েল অবজেক্টটি তুলে আনা, তারপর শুধু যে প্রপার্টিগুলো আপডেট করা দরকার সেগুলো পরিবর্তন করা এবং সবশেষে সেভ করা।
💻 Code Implementation (As per lecture):
public async Task<Person> UpdatePerson(Person person)
{
// ১. ডাটাবেস থেকে রিয়েল অবজেক্ট তুলে আনা
Person? matchingPerson = await _db.Persons.FirstOrDefaultAsync(temp => temp.PersonId == person.PersonId);
if (matchingPerson == null)
return person; // না পেলে আগেরটাই ফেরত
// ২. ম্যানুয়ালি প্রপার্টিগুলো আপডেট করা
matchingPerson.PersonName = person.PersonName;
matchingPerson.Email = person.Email;
matchingPerson.DateOfBirth = person.DateOfBirth;
matchingPerson.Gender = person.Gender;
matchingPerson.CountryId = person.CountryId;
matchingPerson.Address = person.Address;
matchingPerson.ReceiveNewsLetters = person.ReceiveNewsLetters;
// ৩. EF Core নিজে থেকেই ট্র্যাক করবে কোন কোন কলাম চেঞ্জ হয়েছে এবং শুধু সেগুলোর জন্যই UPDATE কুয়েরি চালাবে
await _db.SaveChangesAsync();
return matchingPerson;
}
🚀 Modern .NET Architecture Notes & Pro Tips
১. The “Smart Update” Trick (Using .CurrentValues.SetValues()):
হাসিব, লেকচারার আপডেট মেথডে ম্যানুয়ালি এক এক করে ১০টি প্রপার্টি ওভাররাইট করেছেন। তোমার মডেলে যদি ২০টি প্রপার্টি থাকে, তবে এটি অনেক বোরিং এবং ভুল হওয়ার সম্ভাবনা থাকে। এর একটি জাদুকরী সমাধান আছে EF Core-এ!
✅ Pro Tip Implementation:
public async Task<Person> UpdatePerson(Person person)
{
Person? matchingPerson = await _db.Persons.FirstOrDefaultAsync(temp => temp.PersonId == person.PersonId);
if (matchingPerson == null) return person;
// ম্যাজিক লাইন! এটি নিজে থেকেই person এর সব ডাটা matchingPerson এ কপি করে দেবে (Primary Key ছাড়া)
_db.Entry(matchingPerson).CurrentValues.SetValues(person);
await _db.SaveChangesAsync();
return matchingPerson;
}
২. Avoiding RemoveRange for Single Delete:
লেকচারার ডিলিট করার সময় RemoveRange এর ভেতরে একটি Where কুয়েরি ব্যবহার করেছেন। এটি কাজ করে ঠিকই, তবে প্রাইমারি কি (Primary Key) দিয়ে একটিমাত্র আইটেম ডিলিট করার জন্য এটি ওভারকিল।
✅ Better Alternative:
public async Task<bool> DeletePersonByPersonId(Guid personId)
{
var person = await _db.Persons.FindAsync(personId); // FindAsync প্রাইমারি কি এর জন্য সবচেয়ে ফাস্ট!
if (person == null) return false;
_db.Persons.Remove(person); // RemoveRange এর বদলে শুধু Remove
return await _db.SaveChangesAsync() > 0;
}
পরবর্তী লেকচারে আমরা দেখবো কীভাবে এই নতুন তৈরি করা রিপোজিটরিগুলোকে Service Layer-এর সাথে কানেক্ট করা যায়, যাতে আমাদের আর্কিটেকচারটি সম্পূর্ণ হয়। তুমি রেডি হলে পরের ট্রান্সক্রিপ্টটি দিতে পারো!