হ্যালো হাসিব! আজকের লেকচারে আমরা শিখবো কীভাবে ASP.NET Core অ্যাপ্লিকেশনে ডাটাবেসের ডাটা এক্সপোর্ট করে CSV (Comma Separated Values) ফাইল জেনারেট করতে হয়।

রিপোর্ট জেনারেশন বা ডাটা মাইগ্রেশনের জন্য CSV ফাইল অত্যন্ত জনপ্রিয় একটি ফরম্যাট। এই কাজের জন্য লেকচারার CsvHelper নামের একটি দারুণ থার্ড-পার্টি NuGet প্যাকেজ ব্যবহার করেছেন। চলো পুরো লেকচারটির একটি কুইক সামারি এবং বিস্তারিত পোস্টমর্টেম করে ফেলি।

📝 Lecture Summary at a Glance

  • The Goal: ডাটাবেস থেকে Persons-এর লিস্ট নিয়ে একটি CSV ফাইল তৈরি করা যাতে ইউজার সেটি ডাউনলোড করতে পারে।
  • The Tool (CsvHelper): এটি .NET-এ CSV ফাইল রিড এবং রাইট করার জন্য সবচেয়ে জনপ্রিয় এবং ফাস্ট একটি লাইব্রেরি।
  • MemoryStream: হার্ডডিস্কে ফিজিক্যাল ফাইল সেভ না করে, র‍্যামের (RAM) ভেতরে ডাটা প্রসেস করে সরাসরি ইউজারের কাছে পাঠানোর জন্য MemoryStream ব্যবহার করা হয়েছে।
  • Writer Classes: MemoryStream-এ ডাটা লেখার জন্য StreamWriter এবং তার ওপর বেস করে CsvWriter তৈরি করা হয়েছে।
  • Cursor Position: ডাটা লেখা শেষ হলে MemoryStream-এর Position = 0 করে দেওয়া হয়, যাতে Controller ফাইলটি একদম শুরু থেকে রিড করতে পারে।
  • File Result: Controller থেকে File() মেথড কল করে MemoryStream-কে ইউজারের ব্রাউজারে ফাইল হিসেবে ডাউনলোড করতে দেওয়া হয়।

🧠 Comprehensive Breakdown & Deep Dive

১. IPersonsService-এ নতুন মেথড অ্যাড করা [Importance: 8/10]

  • The “Why”: আমরা চাইলেই Controller-এর ভেতরে CSV বানানোর কোড লিখতে পারতাম, কিন্তু Clean Architecture অনুযায়ী ডাটা প্রসেসিংয়ের সব লজিক Service Layer-এ থাকতে হবে।
  • রিটার্ন টাইপ: মেথডটি একটি MemoryStream রিটার্ন করবে, কারণ এটি ফাইলের বাইট (bytes) রিপ্রেজেন্ট করে যা Controller খুব সহজেই ফাইলে কনভার্ট করতে পারে।
// IPersonsService.cs
Task<MemoryStream> GetPersonsCSV();
 

২. Service Layer-এ CsvWriter ইমপ্লিমেন্টেশন [Importance: 10/10]

  • The “Why”: CsvHelper সরাসরি ডাটাবেসের অবজেক্ট থেকে CSV জেনারেট করতে পারে। এর জন্য CultureInfo.InvariantCulture ব্যবহার করা হয় যাতে কমা (,) বা ফুলস্টপ (.) এর মতো যতিচিহ্নগুলো গ্লোবাল স্ট্যান্ডার্ড অনুযায়ী বসে।
  • leaveOpen: true কেন? বাই-ডিফল্ট, StreamWriter বা CsvWriter যখন ডিসপোজ (Dispose) হয়, তখন তারা তাদের পেছনের MemoryStream-টিকেও বন্ধ (Close) করে দেয়। কিন্তু আমাদের তো এই MemoryStream Controller-এর কাছে পাঠাতে হবে! তাই leaveOpen: true দিলে স্ট্রিমটি ওপেন থাকে।

💻 Code Implementation (Modern .NET 10 / C# 12 Approach): লেকচারে দেখানো কোডটি আমরা আধুনিক C#-এর using ডিক্লারেশন দিয়ে আরও ক্লিন এবং মেমোরি-সেফ করে লিখবো:

using CsvHelper;
using System.Globalization;
using System.IO;
 
public async Task<MemoryStream> GetPersonsCSV()
{
    // ১. ডাটাবেস থেকে Persons ডাটা ফেচ করা
    var persons = await _db.Persons
                           .Include(p => p.Country)
                           .ToListAsync();
                           
    var personResponses = persons.Select(p => p.ToPersonResponse()).ToList();
 
    // ২. MemoryStream তৈরি করা
    var memoryStream = new MemoryStream();
    
    // ৩. StreamWriter এবং CsvWriter তৈরি করা (leaveOpen: true মাস্ট!)
    var streamWriter = new StreamWriter(memoryStream, leaveOpen: true);
    var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture, leaveOpen: true);
 
    // ৪. CSV ফাইলে ডাটা লেখা
    csvWriter.WriteHeader<PersonResponse>(); // প্রথম লাইনে কলামের হেডিং লিখবে
    csvWriter.NextRecord(); // নতুন লাইনে যাবে
    await csvWriter.WriteRecordsAsync(personResponses); // বাকি সব ডাটা লিখবে
 
    // ৫. Memory Flush করা এবং Stream-এর পজিশন শুরুতে নিয়ে আসা
    await csvWriter.FlushAsync();
    memoryStream.Position = 0; // এটি খুবই গুরুত্বপূর্ণ! না হলে ফাইল ফাঁকা আসবে।
 
    return memoryStream;
}
 

৩. Controller থেকে CSV ডাউনলোড করানো [Importance: 9/10]

  • The “Why”: Service থেকে পাওয়া MemoryStream-কে ইউজারের ব্রাউজারে ফাইল হিসেবে পুশ করার জন্য ASP.NET Core-এর বিল্ট-ইন File() রেজাল্ট ব্যবহার করা হয়।

💻 Code Implementation (PersonsController.cs):

[HttpGet]
[Route("[action]")]
public async Task<IActionResult> PersonsCSV()
{
    // Service থেকে MemoryStream নিয়ে আসা
    MemoryStream memoryStream = await _personsService.GetPersonsCSV();
 
    // File() মেথডের মাধ্যমে ডাউনলোড রেসপন্স পাঠানো
    // লেকচারার "application/octet-stream" ব্যবহার করেছেন, তবে "text/csv" হলো সঠিক MIME টাইপ।
    return File(memoryStream, "text/csv", "persons.csv");
}
 

৪. Default CsvHelper-এর সীমাবদ্ধতা [Importance: 7/10]

  • The “Why”: তুমি যখন WriteRecordsAsync দিয়ে সরাসরি একটি অবজেক্ট লিস্ট পাস করো, CsvHelper নিজে থেকে সবগুলো প্রপার্টি (Properties) সিরিয়াল অনুযায়ী ফাইলে লিখে দেয়।
  • সমস্যা: অনেক সময় তুমি হয়তো PersonId ফাইলে দেখাতে চাও না, অথবা DateOfBirth-এর ফরম্যাট পরিবর্তন করে dd-MMM-yyyy করতে চাও। ডিফল্টভাবে এই কাস্টমাইজেশন করা যায় না। লেকচারার ভিডিওর শেষে এই সমস্যার কথাই বলেছেন, যা পরের লেকচারে কাস্টম ম্যাপিংয়ের (Custom Class Mapping) মাধ্যমে সমাধান করা হবে।

🚀 Best Practices & .NET 10 Tips

১. Correct MIME Type: লেকচারার Controller-এ application/octet-stream ব্যবহার করেছেন। এটি একটি জেনেরিক বাইনারি ফাইলের টাইপ। CSV ফাইলের জন্য স্ট্যান্ডার্ড এবং সঠিক MIME টাইপ হলো text/csv। এটি ব্যবহার করলে ইউজারের ব্রাউজার বা অপারেটিং সিস্টেম সহজেই বুঝতে পারে যে এটি একটি এক্সেল/সিএসভি ফাইল।

২. Memory Management (FlushAsync): C#-এ স্ট্রিম (Stream) নিয়ে কাজ করার সময় সবসময় Flush() বা FlushAsync() কল করা উচিত, বিশেষ করে Position = 0 করার আগে। এতে বাফারে থাকা শেষ বিটের ডাটাগুলোও ফিজিক্যালি স্ট্রিমে রাইট হয়ে যায়।

৩. Handling Massive Data (IAsyncEnumerable): যদি তোমার ডাটাবেসে ১ লাখ ইউজার থাকে, তবে পুরো লিস্ট আগে মেমোরিতে (ToList()) আনলে র‍্যাম ওভারফ্লো (Out of Memory) হতে পারে। আধুনিক .NET-এ এই ধরনের বড় এক্সপোর্টের জন্য IAsyncEnumerable ব্যবহার করা হয়, যা ডাটাবেস থেকে একটি একটি করে রো (Row) আনে এবং সাথে সাথে ফাইলে রাইট করে। এটি অনেক বেশি স্কেলেবল।

পরবর্তী লেকচারে আমরা শিখবো কীভাবে CsvHelper-এ কাস্টম ম্যাপার (ClassMap) ব্যবহার করে নির্দিষ্ট কিছু কলাম হাইড করা বা ফরম্যাট করা যায়। তুমি রেডি হলে পরের ট্রান্সক্রিপ্টটি দিতে পারো!