হাসিব, চলো তোমার কোর্সের ৯৯ নম্বর লেকচারটি নিয়ে বিস্তারিত আলোচনা করি। আউটলাইন অনুযায়ী এটি (Section 8: Views - MVC Architecture Pattern) এর অংশ, যার শিরোনাম “Strongly Typed Views with Multiple Models”

আগের লেকচারগুলোতে আমরা দেখেছি কীভাবে একটি View-কে একটিমাত্র Model-এর সাথে বাইন্ড করতে হয়। কিন্তু যদি তোমার View-তে একই সাথে দুটি ভিন্ন Model-এর (যেমন- Person এবং Product) ডেটা দেখানোর প্রয়োজন হয়, তখন কী করবে? এই লেকচারে ঠিক সেই সমস্যার সমাধান হিসেবে “Wrapper Model” বা “ViewModel” প্যাটার্ন নিয়ে বিস্তারিত আলোচনা করা হয়েছে।

নিচে পুরো লেকচারটির বিস্তারিত ব্রেকডাউন দেওয়া হলো:


📝 Summary (সারসংক্ষেপ)

  • The Problem: Razor View-এর @model ডিরেক্টিভ শুধুমাত্র একটি নির্দিষ্ট Model Class-কে সাপোর্ট করে। অর্থাৎ, তুমি একসাথে Person এবং Product দুটি মডেল পাঠাতে পারবে না।
  • The Solution (Wrapper Model / ViewModel): এই সীমাবদ্ধতা দূর করার জন্য একটি নতুন ক্লাস (Wrapper Class) তৈরি করা হয়, যার ভেতরে প্রোপ্রার্টি হিসেবে Person এবং Product উভয় মডেলের রেফারেন্স থাকে।
  • Implementation: Controller থেকে ওই Wrapper Class-এর অবজেক্ট তৈরি করে View-তে পাঠানো হয়। View তখন সেই Wrapper Class-কে নিজের @model হিসেবে রিসিভ করে।
  • Result: View-এর ভেতরে @Model.PersonData.Name এবং @Model.ProductData.ProductName এর মাধ্যমে একই সাথে দুটি ভিন্ন মডেলের ডেটা অ্যাক্সেস করা যায়।

📌 The Problem: Multiple Models in One View [Priority: 9/10]

কেন এটি সরাসরি সম্ভব নয়? ASP.NET Core MVC-এর আর্কিটেকচার অনুযায়ী, একটি View একটি নির্দিষ্ট সময়ে কেবলমাত্র একটি Model-এর সাথেই টাইটলি কাপলড (Tightly Coupled) হতে পারে।

ধরে নাও, তোমার একটি ড্যাশবোর্ড পেজ আছে যেখানে তুমি ইউজারের প্রোফাইল (Person মডেল) এবং তার কেনা একটি প্রোডাক্টের (Product মডেল) তথ্য দেখাতে চাও। তুমি কখনোই Controller থেকে এভাবে ডেটা পাঠাতে পারবে না:

// ❌ Error: Controller থেকে একসাথে দুটি মডেল পাঠানো যায় না
return View(person, product); 
 

এবং View-তেও এভাবে লেখা যায় না:

<!-- ❌ Error: একাধিক @model ডিরেক্টিভ সাপোর্ট করে না -->
@model Person
@model Product
 

এই সমস্যা সমাধানের জন্যই Wrapper Model বা ViewModel এর কনসেপ্ট এসেছে।


📌 Step 1: Creating the Individual Models [Priority: 7/10]

প্রথমে আমাদের দুটি আলাদা মডেল ক্লাস তৈরি করতে হবে। Person ক্লাস আমাদের আগে থেকেই আছে, তাই আমরা শুধু Product ক্লাসটি তৈরি করব।

// Models/Product.cs
namespace MyWebApp.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
    }
}
 

📌 Step 2: Creating the Wrapper Model (ViewModel) [Priority: 10/10]

এবার Models ফোল্ডারে আমরা একটি নতুন ক্লাস তৈরি করব। ইনস্ট্রাক্টর এর নাম দিয়েছেন PersonAndProductWrapperModel (রিয়েল-ওয়ার্ল্ড প্রজেক্টে একে সাধারণত PersonProductViewModel বলা হয়)।

এই ক্লাসের কাজ হলো অন্য মডেলগুলোকে নিজের ভেতরে ধারণ করা (Wrap করা)।

// Models/PersonAndProductWrapperModel.cs
namespace MyWebApp.Models
{
    public class PersonAndProductWrapperModel
    {
        // Person মডেলের জন্য একটি প্রোপার্টি
        public Person PersonData { get; set; }
        
        // Product মডেলের জন্য একটি প্রোপার্টি
        public Product ProductData { get; set; }
    }
}
 

📌 Step 3: Controller Logic (Supplying the Wrapper) [Priority: 10/10]

এবার HomeController-এ একটি নতুন Action Method তৈরি করব। এখানে আমরা Person এবং Product উভয়ের ডেটা ইনিশিয়ালাইজ করে আমাদের তৈরি করা WrapperModel-এর ভেতরে ঢুকিয়ে তারপর View-তে পাঠাবো।

// Controllers/HomeController.cs
[Route("PersonWithProduct")]
public IActionResult PersonWithProduct()
{
    // ১. Person ডেটা রেডি করা
    Person person = new Person { Name = "Hasib", Gender = "Male" };
    
    // ২. Product ডেটা রেডি করা
    Product product = new Product { ProductId = 1, ProductName = "Laptop" };
 
    // ৩. Wrapper Model-এর অবজেক্ট তৈরি করে ডেটা অ্যাসাইন করা
    PersonAndProductWrapperModel wrapperModel = new PersonAndProductWrapperModel
    {
        PersonData = person,
        ProductData = product
    };
 
    // ৪. View-তে Wrapper Model পাঠানো
    return View(wrapperModel);
}
 

📌 Step 4: Accessing Multiple Models in the View [Priority: 10/10]

এখন আমরা PersonWithProduct.cshtml ফাইলটি তৈরি করব। এর একদম শুরুতে @model হিসেবে আমাদের তৈরি করা Wrapper Class-টির নাম বলে দিতে হবে।

<!-- Views/Home/PersonWithProduct.cshtml -->
 
@using MyWebApp.Models
<!-- View কে Wrapper Model এর সাথে বাইন্ড করা হচ্ছে -->
@model PersonAndProductWrapperModel
 
<div class="page-content">
    
    <!-- Person এর ডেটা প্রিন্ট করা হচ্ছে -->
    <div class="box w-50 float-left">
        <h3>Person Details</h3>
        <table class="table w-100">
            <tr>
                <td>Name:</td>
                <!-- @Model এর মাধ্যমে PersonData প্রোপার্টিতে ঢুকছি -->
                <td>@Model.PersonData.Name</td>
            </tr>
            <tr>
                <td>Gender:</td>
                <td>@Model.PersonData.Gender</td>
            </tr>
        </table>
    </div>
 
    <!-- Product এর ডেটা প্রিন্ট করা হচ্ছে -->
    <div class="box w-50 float-left">
        <h3>Product Details</h3>
        <table class="table w-100">
            <tr>
                <td>Product ID:</td>
                <!-- @Model এর মাধ্যমে ProductData প্রোপার্টিতে ঢুকছি -->
                <td>@Model.ProductData.ProductId</td>
            </tr>
            <tr>
                <td>Product Name:</td>
                <td>@Model.ProductData.ProductName</td>
            </tr>
        </table>
    </div>
 
</div>
 

কিভাবে কাজ করছে?

  • @Model এখানে PersonAndProductWrapperModel-কে রিপ্রেজেন্ট করছে।
  • যখন তুমি @Model.PersonData.Name লিখবে, তখন এটি Person ক্লাসের ডেটা আনবে।
  • যখন তুমি @Model.ProductData.ProductName লিখবে, তখন এটি Product ক্লাসের ডেটা আনবে।
  • Visual Studio-তে তুমি পুরো প্রসেসটিতে ১০০% Intellisense সাপোর্ট পাবে।

⌨️ Editor Shortcut (Code Formatting)

ইনস্ট্রাক্টর HTML টেবিল কপি-পেস্ট করার পর কোড এলাইনমেন্ট (Formatting) ঠিক করার জন্য একটি শর্টকাট ব্যবহার করেছেন:

  • Visual Studio (Windows): Ctrl + K, তারপর Ctrl + D (Format Document)
  • Visual Studio Code (Windows): Shift + Alt + F (Format Document)

⭐ Best Practices (Modern .NET 10 Context)

  1. Use “ViewModel” Naming Convention: ইনস্ট্রাক্টর ক্লাসটির নাম দিয়েছেন …WrapperModel। কিন্তু আধুনিক C# প্রজেক্টে এই ধরনের ক্লাসকে সব সময় ViewModel বলা হয়। এর নাম হওয়া উচিত ছিল PersonProductViewModel। তোমার প্রজেক্টে তুমি সব সময় ViewModel সাফিক্স ব্যবহার করবে (যেমন- DashboardViewModel, RegisterViewModel)।
  2. Folder Structure for ViewModels: ViewModel-গুলোকে কখনোই ডাটাবেজের আসল Model-এর সাথে (যেমন Models ফোল্ডারে) রাখা উচিত নয়। প্রোজেক্টে ViewModels নামে আলাদা একটি ফোল্ডার তৈরি করে সেখানে রাখা উচিত। এটি Separation of Concerns বজায় রাখে।
  3. C# 12/13 Object Initializer Syntax (Modern Way): Controller-এ তুমি চাইলে C# এর আধুনিক টার্গেট-টাইপড new() এক্সপ্রেশন ব্যবহার করে কোড আরও ক্লিন করতে পারো:
// Modern Controller code
return View(new PersonProductViewModel
{
    PersonData = new() { Name = "Hasib", Gender = "Male" },
    ProductData = new() { ProductId = 1, ProductName = "Laptop" }
});