Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC Conditional ViewModel Abstraction

I am new to ASP.NET MVC and I am stuck on a point. I am working on a classified site. My situation is, I have a lot of categories in which a user can post their ads and each ad category have different View. I have created a Controller Action like

 public ActionResult PostAd(string CategoryName, string SubCategoryName)
 {
        if(categoryName == "Vehicle" && SubCategoryName == "Cars")
        {
             var model = new CarAdViewModel();

             // set CarAdViewModel properties...

             return View("CarAdCreateView", model);
        }
        else if(categoryName == "Vehicle" && SubCategoryName == "Bikes")
        {
            var model = new BikeAdViewModel();

             // set BikeAdViewModel properties...

             return View("BikeAdViewModel", model);
        }
        else if(categoryName == "Property" && SubCategoryName == "RentHouse")
        {
             var model = new RentHouseAdViewModel();

             // set RentHouseAdViewModel properties...

             return View("RentHouseAdViewModel", model);                 
        }
        else................... so on and so on
 }

My problem is I have huge number of Categories and Sub Categories almost 60+. And if I keep on coding like above for 60+ categories and subcategories, my PostAd method is going to blast and become unmanageable.

Please tell me some best practice or pattern which can bring me out of this problem.

like image 862
Usman Khalid Avatar asked Aug 13 '26 07:08

Usman Khalid


2 Answers

Unfortunately, some of what you are doing cannot be avoided. There needs to be some form of model and view selection based on category.

Use a factory pattern. Create a base class:

public abstract class BaseCategory
{
  public abstract string GetViewName();
  public abstract Object CreateModelFromFormData();
}

For each category, create a sub-class derived from BaseCategory and implement the abstract functions.

In your action, do the following:

public ActionResult PostAd(string categoryName, string subCategoryName)
{
  BaseFactory factory;
  if (categoryName == "Vehicle")
  {
    if (subCategoryName == "Cars")
    {
      factory = new CarsFactory();
    }
    else ...
  }
  else ...

  return View(factory.GetViewName(), factory.CreateModelFromFormData());
}

I have a couple reasons for this schema:

  1. I am purposefully using if/else for the factory selection. Your controller is going to be created and re-created for every action call. So pre-populating a list will constantly and needlessly create objects for categories that will not be selected. A simple if/else will be more efficient. If you want to prevent the if/else, you can put your factories in a Dictionary and select based on the categories, but that would be a lot of needless constructor actions.

  2. I made the CreateModelFromFormData a function because I assume you'll need to copy data from the posted form data. This may require passing in data, but I left the function parameterless.

  3. I used base/derived classes because the copying of the form data will probably need to be custom from the model being created and the form data being posted. Also, saving to persistent storage (file or database) may be category-specific as well.

like image 108
Matt Houser Avatar answered Aug 14 '26 21:08

Matt Houser


It would be one of some possible solutions

public class PostAdData
{
    public string CategoryName;
    public string SubCategoryName;
    public string ViewName;
    public Type Model;
}

public class PostController : Controller
{
    private readonly List<PostAdData> _theData;

    public HomeController()
    {
        _theData = InitializeData();
    }


    public ActionResult PostAd(string categoryName, string subCategoryName)
    {
        var data = _theData.FirstOrDefault(c => c.CategoryName == categoryName && c.SubCategoryName == subCategoryName);
        if (data != null)
        {
            var model = Activator.CreateInstance(data.Model);
            return View(data.ViewName, model);
        }
        return View("Error");
    }

    [NonAction]
    public List<PostAdData> InitializeData()
    {
        var result = new List<PostAdData>
                         {
                             new PostAdData
                                 {
                                     CategoryName = "Vehicle",
                                     SubCategoryName = "Cars",
                                     ViewName = "CarAdCreateView",
                                     Model = typeof (CarAdViewModel)
                                 }
                         };
        return result;
    }
}
like image 21
Abbas Amiri Avatar answered Aug 14 '26 22:08

Abbas Amiri