user2755273
user2755273

Reputation: 31

Selecting from table name passed as argument in MVC Repository Pattern

I am working on a MVC4 project which makes use of Unit of Work and Repository patterns. I have around 25 tables which are only read only tables for Dropdown or Multiselect list. The list might increase in future as we add new modules. I was wondering if I can implement a generic repository which returns a select or multiselect list depending upon table name (or some alias) I pass a an argument to the function.

For eg.

public class GenericListReopsitory<'T> where T is Entity {

public SelectList GetDropDownList() {

}

public MultiSelectList GetMultiSelectList() {

}

}

and then to access in Controller,

ViewBag.RoleDropDownList = Uow.GenericListObj<'Role_Master>.GetDropDownList()

Is it possible to implement something like this in repository pattern?

Upvotes: 1

Views: 764

Answers (1)

melkisadek
melkisadek

Reputation: 1053

It is possible to do both DropDown and MultiSelect Lists through a generic repository.

I've already answered my own question about DropDowns so I won't repeat that.

A MultiSelect list uses the same technique for a blank list but you need something else if you want to edit the list whilst showing the current selections (as with the above link, I use chosen.js to render the MultiSelect input box).

Models

There is a ViewModel for the MultiSelectList which I call from the main ViewModel. The validation is held in a separate file.

MultiSelect ViewModel

public class MultiSelectViewModel
{
    public MultiSelectList Items { get; set; }
}

Entity ViewModel

public partial class InterventionTypeEditViewModel
{
    public int interventionTypeID { get; set; }
    public string interventionType { get; set; }
    public MultiSelectViewModel outcomeID { get; set; }
}

Validation

[MetadataTypeAttribute(typeof(InterventionTypeEditViewModelMetaData))]
public partial class InterventionTypeEditViewModel
{

}

public class InterventionTypeEditViewModelMetaData
{
    [Key]
    [ScaffoldColumn(false)]
    [HiddenInput(DisplayValue = false)]
    public int interventionTypeID { get; set; }

    [Required]
    [Display(Name = "Intervention Type")]
    public string interventionType { get; set; }

    [Display(Name = "Outcome")]
    [UIHint("_multiEdit")]
    public string outcomeID { get; set; }

}

Editor Template

The [UIHint("_multiEdit")] points to the following editor template. The class attributes are for chosen.js and the Twitter Bootstrap framework.

@model WhatWorks.ViewModels.MultiSelectViewModel

@Html.DropDownList("", Model.Items, null, new { @class = "chosen input-xxlarge", multiple = "multiple", })

Controller

In the controller I call the repository twice - once for the controllers owner entity (in this case tInterventionType) and once for the entity that will supply our MultiSelectList values (tOutcome). The _repo instance refers to the generic type parameter T in the standard generic CRUD, Get and GetById methods. The _multi instance uses a generic type parameter O to differentiate the entity in the repository. This may not be best practice (advice welcome) but it works.

    private readonly IAdminRepository<tInterventionType> _repo;
    private readonly IAdminRepository<tOutcome> _multi;

    public InterventionTypeController(IAdminRepository<tInterventionType> _repo,
                                        IAdminRepository<tOutcome> _multi)
    {
        this._repo = _repo;
        this._multi = _multi;
    }

Then in the GET request for the Edit action, selectFrom returns all the items for the select list whist currentSelect returns the items that are already selected. The MultiSelectViewModel is then populated using this MultiSelectList constructor.

    // GET: /InterventionType/Edit/5

    public ActionResult Edit(int id = 0)
    {            
        var selectFrom = _multi.Get();
        IEnumerable<int> currentSelect = from o in selectFrom
                            where o.tInterventionType.Any(m => m.interventionTypeID == id)
                            select o.outcomeID;

        MultiSelectViewModel outcome = new MultiSelectViewModel
        {
            Items = new MultiSelectList(selectFrom, "outcomeID", "outcome", currentSelect)
        };

        InterventionTypeEditViewModel a = GetUpdate(id);
        a.outcomeID = outcome;
        if (a == null)
        {
            return HttpNotFound();
        }
        return View(a);
    }

Repository

The method in the repository is the same as a standard Get() but the generic type parameter O refers to the model that is passed

    public IEnumerable<O> GetMultiSelectEntity<O>()
                            where O : class
    {
        return context.Set<O>().ToList();
    }

View

As with the standard DropDown, the view renders the MultiSelectList via an Html.Editor which picks up the Editor Template from the UIHint annotation on the validation file.


Adding/Editing

To add/edit the MultiSelect I use a separate generic Find in the repository to return the child collection object. The selected items are then added in the controller.

Repository

Return an entity for the child collection using a different generic class (O).

    public O GetMulti<O>(int id) 
                    where O : class
    {
        return context.Set<O>().Find(id);
    }

Controller

To create a new instance of the parent object, Insert the parent and then iterate over the collection of child objects, adding each in turn.

    [HttpPost]
    public ActionResult Create(InterventionTypeAddViewModel model) 
    {
        if (ModelState.IsValid)
        {
            var a = new tInterventionType();
            a.InjectFrom(model);
            _repo.Insert(a);

            foreach (var outcomeId in model.outcome)
            {
                tOutcome o = _repo.GetMulti<tOutcome>(outcomeId);
                a.tOutcome.Add(o as tOutcome);                    
            }

            _repo.Save();
            return RedirectToAction("Index");
        }

        return View(model);
    }

To edit an existing parent object, clear the child collection and add the currently selected items as per the create method.

    [HttpPost]
    public ActionResult Edit(InterventionTypeUpdateViewModel model, int id)
    {
        if (ModelState.IsValid)
        {
            var a = _repo.GetById(id);
            a.InjectFrom(model);
            a.interventionTypeID = id;
            _repo.Update(a);

            //clear the child collection (outcome on interventionType)
            a.tOutcome.Clear();

            //Add current selection
            foreach (var outcomeId in model.outcomeID)
            {
                tOutcome o = _repo.GetMulti<tOutcome>(outcomeId);
                a.tOutcome.Add(o as tOutcome); 
            }

            _repo.Save();
            return RedirectToAction("Index");
        }
        return View(model);
    }

Upvotes: 0

Related Questions