Reputation: 31
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
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).
There is a ViewModel for the MultiSelectList
which I call from the main ViewModel. The validation is held in a separate file.
public class MultiSelectViewModel
{
public MultiSelectList Items { get; set; }
}
public partial class InterventionTypeEditViewModel
{
public int interventionTypeID { get; set; }
public string interventionType { get; set; }
public MultiSelectViewModel outcomeID { get; set; }
}
[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; }
}
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", })
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);
}
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();
}
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.
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.
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);
}
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