Mike Smith
Mike Smith

Reputation: 642

SelectList for DB Value

Using MVC4, EF5 to develop on top of legacy database and code. We have a column (ex. productcolor) in the database that has a distinct set of values. The values can be 'red', 'green', or 'blue'. The DB column is a varchar(20). I can't change the database (yes, I've read about 'enums' - but can't touch the DB).

I would like to create some sort of common object that always returns a select list of these 3 values whenever a form is used to create/edit a new item.

Right now, I have a couple of [NotMapped] classes in my model

    [NotMapped]
    public string ProductColorSelected {
        get { return productcolor; }
        set { productcolor = value; }        
    }

    [NotMapped]
    public IEnumerable<SelectListItem> ProductColors { get; set; }

and then I manually create the SelectList in the Controller before passing to the view

     product.ProductColors = new[]
     {
                new SelectListItem { Value = "Red", Text = "Red" },
                new SelectListItem { Value = "Green", Text = "Green" },
                new SelectListItem { Value = "Blue", Text = "Blue" },
      };

and the view

@Html.DropDownListFor(model => model.ProductColorSelected , Model.ProductColors)

This works but I need to create this select list on every controller class that uses it (Edit, Create) both in POST and GET. Not really following DRY but I'm not sure of a better way.

Also, would the answer change if I had another table that stored the 3 values that could be used for that column. Instead of creating the select list from above, I would get the values from a lookup table (we have both scenarios in our application)?

Thanks!

Upvotes: 0

Views: 524

Answers (1)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038770

One possibility is to write a custom action filter which will execute after each action and populate the ProductColors property on your model:

public class PopulateProductColorsAttribute: ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResultBase;
        if (viewResult == null)
        { 
            // the controller action didn't return a view result => no need to go any further
            return;
        }

        var model = viewResult.Model as SomeModel;
        if (model == null)
        {
            // The controller action didn't pass a model containing the ProductColors property => no need to go any further
            return;
        }

        // now populate the ProductColors property. Of course here you could do a db lookup or whatever
        model.ProductColors = new[]
        {
            new SelectListItem { Value = "Red", Text = "Red" },
            new SelectListItem { Value = "Green", Text = "Green" },
            new SelectListItem { Value = "Blue", Text = "Blue" },
        };
    }
}

and now all that's left is decorate all controller actions that need this with the custom action filter:

[PopulateProductColors]
public ActionResult Index()
{
    SomeModel model = ...
    return View(model);
}

[PopulateProductColors]
[HttpPost]
public ActionResult Index(SomeModel model)
{
    ...
}

or register it as a global action filter in which case it will apply to all your controller actions.

Upvotes: 1

Related Questions