Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Reducing repetitive code

I have just started using and am falling in love with MVC design pattern.

My only pet peeve with it is that it seems to produce a lot of repetitive code. For example.

I have a standard MVC App with my DB (models) in one project, separated from my controllers / views / viewmodels in another, again separated from my test methods in another. All working great.

Models: Now, I have a bunch of nice EF4 classes in my DB project, which I have to use ViewModels for in my real project to access my data. No problem here.

Controllers: However, every controller I have, essentially does the very same thing. It gets and sets the data from the ViewModels so while each controller is different in that it gets only different data, they are all essentially doing the very same job, in the very same way. (I currently have 9, but this could easily explode to well over 50).

For example:

public class Dummy1Controller : Controller
{
    private MyProj.Data.Entities _entities = new Data.Entities();
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2();

    //GET: /Customers/
    public ActionResult Index()
    {
        if (_entities.table1.Count() == 0) return View();

        var pastObj = _entities.table1.First();
        return View(new Table1ViewModel()
        {
            Id = pastObj.Id,
            FirstName = pastObj.FirstName,
            LastName = pastObj.LastName,
            .
            .
            .
            .
        });
    }
}

public class Dummy2Controller : Controller
{
    private MyProj.Data.Entities _entities = new Data.Entities();
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2();

    //GET: /Vehicles/
    public ActionResult Index()
    {
        if (_entities.table2.Count() == 0) return View();

        var pastObj = _entities.table2.First();
        return View(new Table1ViewModel()
        {
            RegNo = pastObj.RegNo,
            Make = pastObj.Make,
            Model = pastObj.Model,
            .
            .
            .
            .
        });
    }
}

public class Dummy3Controller : Controller
{
    private MyProj.Data.Entities _entities = new Data.Entities();
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2();

    //GET: /Invoices/
    public ActionResult Index()
    {
        if (_entities.table3.Count() == 0) return View();

        var pastObj = _entities.table3.First();
        return View(new Table1ViewModel()
        {
            InvNo = pastObj.InvNo,
            Amount = pastObj.Amount,
            Tax = pastObj.Tax,
            .
            .
            .
            .
        });
    }
}

Views: Every view generated from the contollers work great. Execpt, that only thing that changes is the data (fields with labels and text boxes). Once again, they all do the very same job (but with different datasets).

@model MyProject.Web.ViewModels.Table1ViewModel

@{
    ViewBag.Title = "Index";
}

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" />

<section id="content">
    <div id="table">
        <div>
            <h2>Customer</h2>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.Id)</div>
            <div class="right">@Html.TextBoxFor(model => model.Id)</div>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.FirstName)</div>
            <div class="right">@Html.TextBoxFor(model => model.FirstName)</div>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.LastName)</div>
            <div class="right">@Html.TextBoxFor(model => model.LastName)</div>
        </div>
        .
        .
        .
        .
    </div>
</section>

@{Html.RenderAction("Index", "FooterPartial");}


--------------------------------------------------------------------------------------


@model MyProject.Web.ViewModels.Table2ViewModel

@{
    ViewBag.Title = "Index";
}

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" />

<section id="content">
    <div id="table">
        <div>
            <h2>Vehicle</h2>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.RegNo)</div>
            <div class="right">@Html.TextBoxFor(model => model.RegNo)</div>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.Make)</div>
            <div class="right">@Html.TextBoxFor(model => model.Make)</div>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.PatientID)</div>
            <div class="right">@Html.TextBoxFor(model => model.Model)</div>
        </div>
        .
        .
        .
        .
    </div>
</section>

@{Html.RenderAction("Index", "FooterPartial");}


--------------------------------------------------------------------------------------


@model MyProject.Web.ViewModels.Table3ViewModel

@{
    ViewBag.Title = "Index";
}

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" />

<section id="content">
    <div id="table">
        <div>
            <h2>Invoice</h2>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.InvNo)</div>
            <div class="right">@Html.TextBoxFor(model => model.InvNo)</div>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.Amount)</div>
            <div class="right">@Html.TextBoxFor(model => model.Amount)</div>
        </div>
        <div class="row">
            <div class="left">@Html.LabelFor(x=>x.Tax)</div>
            <div class="right">@Html.TextBoxFor(model => model.Tax)</div>
        </div>
        .
        .
        .
        .
    </div>
</section>

@{Html.RenderAction("Index", "FooterPartial");}

Problem: I want to make a single controller, and make it dynamic. So that it can read data from different view models. (Why have 9 or 50 controllers esentially doing the same job)

Then I want to do likewise with the views. So that different fields can be dynamically generated. (Why have 9 or 50 views all doing the same job). If the view is based on the controller, the view should be able to change based on its properties.

Essentially all I want to do is find a way to tell the controller to read from viewmodel X or VM - Y or VM - Z ect and it should be able to generate the properties, retreive the associated data, and pass it to the view, which upon receiving, will generate the fields with labels and text boxes.

I guess I want to know if there is any way to do this using reflection. As the view models are basic classes with simple properties. One could potentially create a base controller class that has a method to read in a specified viewmodel object, get its properties, read in also an associated table and match up the fields in that table with the properties in the class. Finally one can pass in the record from the table to display. The view then can be generated automatically based on this using some kind of razor, c# or javascript.

Any taughts on if this is possible or not would be welcome.

like image 893
Francis Rodgers Avatar asked May 22 '12 15:05

Francis Rodgers


2 Answers

You can use AutoMapper to remove all the code for copying values between models / entities.

Also, consider using layouts, Data Annotation attributes and templates (with Html.DisplayFor and Html.EditorFor) to shrink your views.

http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

You could investigate a possibility of creating a generic base controller class, that will take the type of Model and Entity and will contain common logic for CRUD operations, but it may be a step too far and hinder your development later on.

like image 147
Jakub Konecki Avatar answered Oct 23 '22 18:10

Jakub Konecki


I think that one dynamic controller would possibly be a step too far - what happens when you make everything generic and then one of your views requires something more complex than a simple map to the database - do you extend your 'generic view controller' to deal with this? Might wind up here.

You should maybe take a look at Automapper to remove some of the repetitive nature of dealing with your viewmodels.

http://automapper.codeplex.com/

like image 23
Paddy Avatar answered Oct 23 '22 18:10

Paddy