হ্যালো হাসিব! আমরা এই লেকচারটিতে PersonsService এর রিফ্যাক্টরিং সম্পন্ন করেছি।

আগের লেকচারে আমরা CountriesService কে ICountriesRepository-এর সাথে কানেক্ট করেছিলাম। এবার আমরা PersonsService কেও একইভাবে IPersonsRepository-এর সাথে কানেক্ট করলাম। এই প্রক্রিয়ায় আমরা সার্ভিস লেয়ার থেকে DbContext কে পুরোপুরি বিদায় দিয়েছি!

চলো লেকচারটির একটি কুইক সামারি এবং বিস্তারিত পোস্টমর্টেম করে ফেলি।

📝 Lecture Summary at a Glance

  • Dependency Updates: PersonsService-এ ApplicationDbContext এবং ICountriesService ডিলিট করে শুধু IPersonsRepository ইনজেক্ট করা হয়েছে।
  • CRUD Methods Update: AddPerson, GetPerson, UpdatePerson এবং DeletePerson মেথডে সরাসরি ডাটাবেস কলের বদলে রিপোজিটরির মেথডগুলো কল করা হয়েছে।
  • Advanced Filtering Update: GetFilteredPersons-এ C# 8.0 এর Switch Expression ব্যবহার করে ডায়নামিকভাবে ল্যাম্বডা এক্সপ্রেশন (Predicate) তৈরি করে রিপোজিটরিতে পাঠানো হয়েছে।
  • LINQ Translation Error: EF Core-এর LINQ-to-SQL ট্রান্সলেশনে StringComparison.OrdinalIgnoreCase সাপোর্ট করে না, তাই এটি রিমুভ করা হয়েছে (কারণ SQL সার্ভারের LIKE অপারেটর ডিফল্টভাবেই Case-Insensitive)।
  • DI Registration: Program.cs-এ নতুন দুটি রিপোজিটরি ইন্টারফেসকে AddScoped দিয়ে রেজিস্টার করা হয়েছে।

🧠 Comprehensive Breakdown & Deep Dive

১. Refactoring GetFilteredPersons (The Power of Switch Expression) [Importance: 10/10]

  • The “Why”: আগে সার্ভিস লেয়ার প্রথমে ডাটাবেস থেকে সব ডাটা মেমোরিতে (RAM) নিয়ে আসতো, তারপর C# দিয়ে ফিল্টার করতো। ১০০০ বা ১০,০০০ ডাটার জন্য এটি মারাত্মক স্লো।
  • The Fix: এখন সার্ভিস লেয়ার একটি ল্যাম্বডা এক্সপ্রেশন তৈরি করে রিপোজিটরির কাছে পাঠায়। রিপোজিটরি সেই এক্সপ্রেশনটি EF Core-কে দেয়, আর EF Core সেটিকে সরাসরি SQL Server-এর WHERE কুয়েরিতে রূপান্তর করে। এতে ডাটাবেস থেকেই ফিল্টার হয়ে শুধুমাত্র প্রয়োজনীয় ডাটাগুলোই মেমোরিতে আসে!

💻 Code Implementation (PersonsService.cs):

public async Task<List<PersonResponse>> GetFilteredPersons(string searchBy, string? searchString)
{
    List<Person> persons = searchBy switch
    {
        // nameof() ব্যবহার করা বেস্ট প্র্যাকটিস, যাতে প্রপার্টি নাম চেঞ্জ হলে এরর না আসে
        nameof(PersonResponse.PersonName) =>
            await _personsRepository.GetFilteredPersons(temp =>
                temp.PersonName.Contains(searchString)),
 
        nameof(PersonResponse.Email) =>
            await _personsRepository.GetFilteredPersons(temp =>
                temp.Email.Contains(searchString)),
 
        // ... অন্যান্য ফিল্টার কন্ডিশন ...
 
        _ => await _personsRepository.GetAllPersons() // Default case
    };
 
    return persons.Select(temp => temp.ToPersonResponse()).ToList();
}
 

২. LINQ to SQL Translation Error [Importance: 9/10]

  • The “Why”: লেকচারার যখন কোড রান করলেন, তখন একটি এরর আসলো: “The LINQ expression… could not be translated”.
  • The Reason: যখন তুমি ল্যাম্বডা এক্সপ্রেশন রিপোজিটরিতে পাঠাও, EF Core সেটিকে SQL কুয়েরিতে কনভার্ট করার চেষ্টা করে। তুমি যদি Contains(searchString, StringComparison.OrdinalIgnoreCase) লেখো, SQL Server জানে না StringComparison জিনিসটা কী! কারণ এটি একটি C# স্পেসিফিক ফিচার।
  • The Fix: SQL Server-এর LIKE অপারেটর (যেমন WHERE Name LIKE '%hasib%') বাই ডিফল্ট Case-Insensitive (অর্থাৎ ছোটহাতের বা বড়হাতের যাই লিখো, সেম ভাবে কাজ করবে)। তাই C#-এর স্পেশাল কেস ইগনোর করার কোডটি রিমুভ করে দিলেই কাজ হয়ে যায়।

৩. Connecting the Dots in Program.cs [Importance: 10/10]

  • The “Why”: তুমি যদি Controller-কে Service এবং Service-কে Repository ব্যবহার করতে দাও, তবে ASP.NET Core-কে জানাতে হবে যে কোন ইন্টারফেসের জন্য কোন ক্লাসটি দিতে হবে। এটি না করলে DependencyResolutionException আসবে।

💻 Code Implementation (Program.cs):

// Repository Registration
builder.Services.AddScoped<ICountriesRepository, CountriesRepository>();
builder.Services.AddScoped<IPersonsRepository, PersonsRepository>();
 
// Service Registration (Already there from previous sections)
builder.Services.AddScoped<ICountriesService, CountriesService>();
builder.Services.AddScoped<IPersonsService, PersonsService>();
 

🚀 Modern Architecture Pro Tips (For Your Project)

১. The Danger of Service Calling Service: লেকচারার GetPersonsCSV মেথডে _personsService.GetAllPersons() কল করেছেন। অর্থাৎ একই ক্লাসের একটি মেথড ওই ক্লাসেরই আরেকটি মেথড কল করছে। এটি ছোট প্রজেক্টে ঠিক আছে, কিন্তু ক্লিন আর্কিটেকচারে এটি একটু অ্যান্টি-প্যাটার্ন। বেটার হলো, তুমি প্রাইভেট কোনো হেল্পার মেথড বানিয়ে সেটিকে দুই জায়গা থেকে কল করো।

২. The “Null” in Unit Tests (The Elephant in the Room): লেকচারার ইউনিট টেস্ট প্রজেক্টে new PersonsService(null) দিয়ে বিল্ড এরর ফিক্স করেছেন। এটি খুবই ডেঞ্জারাস! তুমি যদি এখন টেস্ট রান করো, সবগুলো টেস্ট NullReferenceException খেয়ে ক্র্যাশ করবে। পরবর্তী লেকচারে আমরা Moq লাইব্রেরি ব্যবহার করে এই null এর জায়গায় রিপোজিটরির ডামি (Mock) অবজেক্ট বসাবো। এটিই হবে আমাদের টেস্টিংয়ের ফাইনাল এবং সবচেয়ে প্রফেশনাল আর্কিটেকচার।

তুমি রেডি হলে পরবর্তী ট্রান্সক্রিপ্টটি দিতে পারো!