Reputation: 59
I built a simple ticket-tracking system with MVC3. For testing purposes, I'd like to implement IOC/DI. Right now my controller interacts directly with the database, which is bad for TDD. I'm quite new to the subject.
Right now my TicketDB.cs Model looks like this:
namespace TicketTracker.Models
{
public class TicketDB
{
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
public class TicketDBContext : DbContext
{
public DbSet<TicketDB> Tickets { get; set; }
}
}
And my TicketController.cs controller looks like this :
namespace TicketTracker.Controllers
{
public class TicketsController : Controller
{
private TicketDBContext db = new TicketDBContext();
//
// GET: /Tickets/
public ViewResult Index()
{
return View(db.Tickets.ToList());
}
//
// GET: /Tickets/Details/5
public ViewResult Details(int id)
{
TicketDB ticketdb = db.Tickets.Find(id);
return View(ticketdb);
}
//
// GET: /Tickets/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Tickets/Create
[HttpPost]
public ActionResult Create(TicketDB ticketdb)
{
if (ModelState.IsValid)
{
db.Tickets.Add(ticketdb);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ticketdb);
}
//
// GET: /Tickets/Edit/5
public ActionResult Edit(int id)
{
TicketDB ticketdb = db.Tickets.Find(id);
return View(ticketdb);
}
//
// POST: /Tickets/Edit/5
[HttpPost]
public ActionResult Edit(TicketDB ticketdb)
{
if (ModelState.IsValid)
{
db.Entry(ticketdb).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ticketdb);
}
//
// GET: /Tickets/Delete/5
public ActionResult Delete(int id)
{
TicketDB ticketdb = db.Tickets.Find(id);
return View(ticketdb);
}
//
// POST: /Tickets/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
TicketDB ticketdb = db.Tickets.Find(id);
db.Tickets.Remove(ticketdb);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
I've watched a bunch of IOC/DI videos, but can't quite figure out how to implement them. I know I have to create an interface, so my controller will be able to interact with both a database, and a static list of data. How would this look?
I was thinking I'd use Ninject
as the DI framework, but I am open to other suggestions.
Upvotes: 2
Views: 768
Reputation: 10722
Using Castle.Windsor
ITicketRepository:
public interface ITicketRepository
{
IList<Ticket> FindAll();
Ticket Find(int id);
void Save(Ticket ticket);
}
TicketEfRepository:
public class TicketEfRepository: ITicketRepository
{
public IList<Ticket> FindAll()
{
var db = new TicketDBContext();
return db.Tickets.ToList();
}
public Ticket Find(int id)
{
var db = new TicketDBContext();
return db.Tickets.Find(id);
}
...
TicketsController:
public class TicketsController : Controller
{
private readonly ITicketRepository _ticketRepository;
public TicketsController(ITicketRepository ticketRepository)
{
_ticketRepository = ticketRepository;
}
public ViewResult Index()
{
return View(_ticketRepository.FindAll());
}
...
ControllerInstaller: (Registers all controllers by convention)
public class ControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(FindControllers().Configure(ConfigureControllers()));
container.Register(
Component
.For<IControllerFactory>()
.ImplementedBy<ControllerFactory>()
.LifeStyle.Singleton
);
}
private ConfigureDelegate ConfigureControllers()
{
return c => c.LifeStyle.Transient;
}
private BasedOnDescriptor FindControllers()
{
return AllTypes.FromAssemblyContaining<TicketsController>()
.BasedOn<IController>()
.If(t => t.Name.EndsWith("Controller"));
}
}
ControllerFactory:
public class ControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public ControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404,
string.Format("The controller for path '{0}' could not be found.",
requestContext.HttpContext.Request.Path));
}
return (IController) kernel.Resolve(controllerType);
}
}
Global.asax:
public class MvcApplication : HttpApplication
{
private static IWindsorContainer _container;
protected void Application_Start()
{
BootstrapContainer();
}
protected void Application_End(object sender, EventArgs e)
{
_container.Dispose();
}
private void BootstrapContainer()
{
_container = new WindsorContainer();
// Registers all components from classes implementing
// IWindsorInstaller when classes defined in the web assembly, i.e.
// ControllerInstaller
_container.Install(FromAssembly.This());
// You can also put this in a separate installer class and register
// by convention
_container.Register(
Component
.For<ITicketRepository>()
.ImplementedBy<TicketEfRepository>()
.LifeStyle.Transient);
ControllerBuilder.Current.SetControllerFactory(
_container.Resolve<IControllerFactory>());
}
}
Upvotes: 1