Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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;
    }
}

You should make this data driven. You create a lookup table that has a compound primary key of category and subcategory. Then it has a table with View in it. Then you simply ad rows for each category/subcategory/view combination.

If you absolutely don't want a database, then you can use a simple hashset or dictionary.

var views = new Dictionary<Tuple<string,string>,string>();

views.Add(new Tuple<string,string>("Vehicle", "Cars"), "CarAdCreateView");

Then in your PostAd you just lookup the correct view.

What a beautiful solution on www.asp.net to my question, here is the link : http://forums.asp.net/t/1923868.aspx/1?ASP+NET+MVC+Conditional+ViewModel+Abstraction

Edit:

My code is :

public class AdsController : Controller
{
     private readonly IAdService _adService;
     public AdsController(IAdService adService)
     {
         _adService = adService;
     }

     public ActionResult PostAd(string Category, string SubCategory)
    {
        //Here I will call
        var strategy = GetStrategy(CategoryName, SubCategoryName);
        strategy.FillModel(_adService );
        return View(strategy.ViewName, strategy.Model);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top