Software Startups – Beware the (ORM) Golden Hammer

Software Startups – Beware the (ORM) Golden Hammer

I’ve lost count of the number of times I’ve heard software architects and senior developers, after much circumlocution involving IT-governance, Enterprise standards, maintainability, reusability, sustainability, and so forth, come to the ‘inescapable’ conclusion that the new software development project “..will be implemented using EntityFramework/nHibernate/[Insert your choice of ORM here].”

Cue many, many hours of technological sleights-of-hand and hair pullings-out.

Fast forward several days/weeks and you have rising concerns that your process is

  • Consuming staggering amounts of memory on the server
  • Inexplicably inflating 100,000 unasked-for instance objects into your process space
  • Making 5 calls to the database whenever you make what you thought was a simple request for Customer.Name
  • Running like a lama

Despite all of this however, your faith remains undiminished.  All these issues will be resolved any moment now, you think to yourself, and you confidently fire off a support request to your ORM software provider, and post a question to your favourite blog-sphere. And sit back and wait.

At this point there is a question I want to ask you, but not just yet, you are not ready and trust me, we have plenty of time.

Soon your phone buzzes and no, it isn’t Sundeep on stack-overflow responding to your query, it’s your mates calling from the pub to ask why you haven’t showed up. Its 6.30pm! Your faith starts to waver, but still you are not ready. Fast forward an hour or two, and you are the only one left in the office, slumped in the gloom, creeping despair illuminated by screen flicker. The cleaner has been and gone, tut-tutting under her breath as she cleaned around you, and you have to resort to  sporadically waving an arm in the air to keep the lights on (sub-text: your brain-time is cheaper than electricity).

But you can do this, you say to yourself, all you need is a triple shot skinny mocha-chino to restore your wavering faith, so you stretch out of your chair and peer out of the window at the Pret across the street, and guess what, its closed. Of course its closed, their target clientele are those nice folks from your IT governance/Enterprise Architecture department, and they’ve looooong since gone home. And to cap it all, the online moderator has closed your question as ‘Not Constructive’.

You sink back in your chair, head in your hands, the rising doubts finally breaking through that surface tension of architectural confidence, and now, now that you are at your lowest ebb, you are finally ready for the question. And the question is this. “What have you done?”

How did you get from “…disrupting the existing market by introducing the next generation crowdFunder app..”, to grubbing about in the tech dumps, endlessly reconfiguring relationships between stock.xml and order.xml, trying to find a freeware Process Viewer download so you can work out whats going wrong, and trying to figure out if array.GetAllCustomerNames() has actually got all customer names, or is just describing HOW it would get all customer names, when it finally gets round to doing it at some most inconvenient time.

Welcome to “should’a done something different with my life”-ville, population: You.

There’s no need to go reflecting on all the points in your life when you could have taken a different turn, and how things might have worked out. It isn’t your fault, you have just become the latest victim of The Golden Hammer, aka ‘when the only tool you have is a hammer, then every problem looks like a nail’. The hammer, in this case being the big-beast third party ORM/Framework mapper that’s now proving to be the bane of your existence.

Ok, so I’m being deliberately provocative. The reasoning behind the decision of my ‘straw man’ architecture board could also be described as ‘the devil you know is better than the devil you don’t..’. An in large corporations, it can be more cost effective to spend as much development resource as necessary on a problem, rather than re-convening a meeting of far-flung architects to try and explain why a totally different architecture should be used in a particular instance (believe me I’ve tried). However, when the context is a small company or startup, the value-proposition is very different.

When those proto-hammers first came out, we were all excited. So we don’t actually have to write any code to pull our data out of storage etc? This product will do it for us? All we have to do is plug it into our new/planned system? Brilliant.

However it soon turned out that the product(s) had limitations whenever you wanted to do something out of the ordinary, so newer and newer versions were released by the vendors, with more and more complicated rules and syntax, and so we began the long, long scramble up Mt. Incomprehensibility. So now, YES you don’t have to write any code to access/update your data store, which is good because now ALL your time is being consumed wrestling the big beast ORM into your system. And IF you do succeed, what have you ended up with? You’ve spent just as much, if not more, development time and resource, and you have created a sluggish, non-optimised, gloriously opaque system with a slumbering uncooperative beast at its core, AND you have now introduced an embedded dependency on a third party product that you didn’t need in the first place.

“But wait!” I hear you cry..

“..we have an expert that can configure our chosen ORM platform/framework with one arm tied behind his back, so it makes sense for us..”

Well that’s great then, but if you have such a Guru, then the chances are the guru-ness also includes being able to craft superb sql data queries, and excellent code, in double quick time. So why didn’t you just empower him/her to do that, why not make use of that talent? Or where on earth did you find an nhibernate guru that can’t also write great code?

“But..”, you protest..

“..what about flexibility of architecture, we need a framework so we can easily adapt to new requirements from the client..”.

Well of course you do. So are you going to spend hours/days/weeks building out this architectural framework, just in case your client decides in a couple of years that they’d like a specific additional functionality? And when that moment comes, do you imagine you would just make an xml configuration change in your super flexible framework, without writing any code whatsoever, and voila! the requirement is satisfied? When, in the history of software development, has that ever been the case? Ok, it’s probably happened a couple of times in a galaxy far, far away but, I respectfully submit, it’s not very likely to happen to you. When your client does request a change, it’ll be something like

“..could we have a ticking clock in the upper right hand side of the screen, so we don’t accidentally wake up the prospective investor at an inconvenient hour..”

And trust me, no amount of mapping/configuration is going to help you with that.

In the meantime, instead of spending time drumming up new customers, or writing software that will enhance existing customer loyalty, you are future proofing for situations that will probably never arise. For an interesting article on why you shouldn’t be doing this, see http://www.agilebuddha.com/agile/agile-principle-simplicity-the-art-of-maximising-the-work-not-done/

In essence, you don’t have to embrace the whole agile, story-boarding, pair-programming, daily stand-up exercise rigmarole, it’s just surely a good idea not to spend all that time future proofing for scenarios that may never arise, especially as if and when they do, they’ll be different from what you imagine.

Returning to the concept of the Golden Hammer, I’m not saying that EntityFramework, nHibernate, and the rest of their ilk are the only tools in your architect’s toolbox hence their usage, nor am I suggesting that your developer has simply chosen those technologies with an eye to skill-ing up for their next role, regardless of the consequences for your project. What I am suggesting however, is that before you bang that ‘nail’ with that hammer, you might be best advised to take a step back and reconsider.

So, with all that said, what are your other options. You could handcraft ALL the code yourself, or you could get an off-the-shelf code generator that just cranks out enough comprehensive code within minutes to get you started. Now every developer worth his salt will reckon they can write faster, leaner, meaner code than any code generator (and they are probably right), and they are happy to spend hours at home in bed, with their laptop, re-writing the same procedure over and over again, proving exactly that. But as a business, you may want the middle ground, a lightning fast solution that gets the project off and running with a comprehensive code framework, but then allows your developer to easily dive in and customise/optimise/replace absolutely anything they want, to their satisfaction. A solution that does NOT involve bundling unnecessary, sluggish and opaque third party dependencies into your finished system. In other words it gives you a helping hand (quickly) whenever you need it, and then, crucially, it steps out of the way!

A quick search for “Code Generators” in my Visual Studio as I write this gives the following results (results filtered for relevance):

CodeSmith Generator [By CodeSmith Tools LLC]

CodeFluent Entities [By SoftFluent]

CodeTrigger Code Generator [By Exotechnic Corporation]

and many more..

Any one of these should get you off to a good start, an initial investigation would confirm for you which of the many available great code generators best suits your style of working, and avoids the pitfalls described above.

So shake off the shackles of ritual, and get your great product/service in front of the customer today.

Database to Unit-Test-Enabled WebApi MVC app in 15 mins

Database to Unit-Test-Enabled WebApi MVC app in 15 mins

Perhaps you’ve just read my previous post Database to full asp.net Mvc app in 5 mins  (or perhaps not) but in any case you find yourself thinking, “..thats all very well but what about testability? I want to know that my approach is testable, and that it supports unit tests, and mocking, and repositories and the like, otherwise the QA team is going to be ALL over me..”

Well, lets see how you can go from an existing database to a unit-test-enabled, asp.net webapi/mvc based app with mockable repositories etc, in just 15 minutes.

[You will need Visual Studio (2012+) and the CodeTrigger For Visual Studio extension (Full Trial, or Professional Version, v4.8.6.7+), Windows 7.1+, .Net Framework 4.6.1+. Once you got that setup, make a note of the time and begin]

Step 1. Start a new CodeTrigger project within Visual Studio, give it a name TestableWebApiMvc, and select the ‘DB to Multi-tier Mvc ..’ wizard. Make sure you click the sub-option ‘Enable Test’, new in 4.8.6.7. This small, innocuous option heralds significant changes in the generated code, specifically support for mockable repositories etc. Click next.

pr_pr1a

Step 2. Select your data source type, and configure the connection settings. Click ‘Connect’ to verify the settings, and click ‘Create’. Here we are using SQL Server, just to vary things since in the last post we used Oracle (and next post we might use MYSQL). We will be using the Northwind test database, feel free to use any other, but you will need to edit your code when we refer to entities in the northwind database such as ‘Categories’ or ‘Products’.

pr_pr2a

pr_pr3b

Step 3. Voila, the multi-tier Visual Studio project is generated and pre-built. (If you don’t have nuget automatic package download enabled, you may have missing package build errors at this stage, in this case, enable nuget automatic package download on your solution, or otherwise download the missing asp.net core packages from nuget, and rebuild. If you do have nuget automatic package download enabled then this is all done for you).

Note that this wizard will attempt to automatically create the identity tables required by Asp.Net identity, in your database. If those tables already exist then they will not be regenerated.

In the ‘Schema Objects’ tab, select any tables you want to be represented in the final application. For this illustration we use the the Categories and the Products table.

pr_pr3a

Step 4. Click on the ‘Business Objects’ tab. The business model is generated from your previous selections and all related business model entities are displayed for your selection, along with their relationships. Select ‘BOCategories’ and ‘BOProducts ‘, as the objects we will be working with in our tests.

pr_pr4a

Step 5. Shoot! Click the red button to generate the selected code classes. Stand back and be happy as CodeTrigger spews out several thousand lines of required code in a matter of seconds! At this point the project/solution will build itself and report happy. If there’s any outstanding tasks, they might be reported a the ‘Things to do’ tab that sometimes pops up, but I find that useful only when I’ve changed some of the default automatic code generation settings (like automatic sql scripting or automatic project file update).

pr_pr5a

Step 6. Hit F5 to build and run. Enjoy the sight of your fully functioning Asp.Net Mvc App. Login using an auto generated login for debug purposes.

pr_pr8a

Check the time, 5 mins! So far so good. We have a fully functioning basic app, albeit auto-generated. Now we have 10 mins left to setup the unit testing framework.

 

Step 7. Add a Unit Test project to the solution

pr_pr9a

 

pr_pr10a_2

 

 

Add references to the business, data and mvc projects to the unit test project.

Add the System.Net.Http, and System.Web.Http references to the unit project.

Its important to get the right references for System.Web.Http and System.Net.Http.

 

On my system System.Web.Http is the one in the packages folder {project }\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45 while the System.Net.Http is from C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1

 

Step 8. Create the mock repositories for testing your code without involving the database. Add a folder called MockRepositories to the Unit Test project and add the following classes to it.

MockRepositoryBase.cs


using System;
using System.Collections;
using System.Collections.Generic;
using TestableWebApiMvc.Data.Interfaces;

namespace UnitTestProject1.MockRepositories
{
    public class MockRepositoryBase<T>
    {
        public virtual T New() { return default(T); }
        public virtual IList<T> SelectAll() { return null; }
        public virtual Int32 SelectAllCount() { return 0; }
        public virtual IDictionary<string, IList<object>> SelectAllByCriteriaProjection(IList<IDataProjection> dataProjection, IList<IDataCriterion> listCriterion, IList<IDataOrderBy> listOrder, IDataSkip dataSkip, IDataTake dataTake) { return null; }
        public virtual IList<T> SelectAllByCriteria(IList<IDataCriterion> listCriterion, IList<IDataOrderBy> listOrder, IDataSkip dataSkip, IDataTake dataTake) { return null; }
        public virtual Int32 SelectAllByCriteriaCount(IList<IDataCriterion> listCriterion) { return 0; }
        public virtual INorthwindDb_BaseData BaseData(T tbase) { return null; }
        public INorthwindDb_TxConnectionProvider ConnectionProvider { get { return null; } set { } }
        public Int32 TransactionCount { get; set; }
    }
}

MockCategoriesRepository.cs


using System;
using System.Collections.Generic;
using System.Linq;
using TestableWebApiMvc.Data;
using TestableWebApiMvc.Data.Interfaces;
using TestableWebApiMvc.Business.Repository.Interfaces;

namespace UnitTestProject1.MockRepositories
{
    public class MockCategoriesRepository :  MockRepositoryBase<IDAOCategories>, ICategoriesRepository
    {
        private IList<IDAOCategories> _categoriesList;
        public MockCategoriesRepository(IList<IDAOCategories> categoriesList)
        {   _categoriesList = categoriesList;   }

        public void Insert(IDAOCategories daoCategories) 
        {   _categoriesList.Add(daoCategories); }

        public void Update(IDAOCategories daoCategories) 
        {
            var category = SelectOne(daoCategories.CategoryID);
            category.CategoryName = daoCategories.CategoryName;
            category.Description = daoCategories.Description;
            category.Picture = daoCategories.Picture;
        }

        public void Delete(IDAOCategories daoCategories)
        {   _categoriesList.Remove(SelectOne(daoCategories.CategoryID));    }

        public IDAOCategories SelectOne(Int32? categoryID) 
        {   return _categoriesList.Single((x) => x.CategoryID == categoryID);   }

        public override IList<IDAOCategories> SelectAllByCriteria(IList<IDataCriterion> listCriterion, 
            IList<IDataOrderBy> listOrder, IDataSkip dataSkip, IDataTake dataTake) 
        { 
            //handle search by id
            if (((DataCriterionEq)listCriterion[0]).PropertyName == "CategoryID")
            {
                int categoryID = (int)((DataCriterionEq)listCriterion[0]).PropertyValue;
                return _categoriesList.Where((x) => x.CategoryID == categoryID).ToList();
            }
            return null; 
        }
    }
}

 

Step 9. Write a couple of basic Unit tests. Expand the UnitTest1 class with your unit tests, here are a couple of tests,  GetCategory_ShouldFindCategory , GetCategory_ShouldNotFindCategory 


using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Http.Results;

using Microsoft.VisualStudio.TestTools.UnitTesting;
using UnitTestProject1.MockRepositories;

using TestableWebApiMvc.Data;
using TestableWebApiMvc.Data.Interfaces;
using TestableWebApiMvc.Mvc.SampleApiControllers;
using TestableWebApiMvc.Mvc.SampleViewModels;

using TestableWebApiMvc.Business.Repository;
using TestableWebApiMvc.Business.Repository.Interfaces;
using TestableWebApiMvc.Business;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public async Task GetCategory_ShouldFindCategory()
        {
            CategoriesController catController = new CategoriesController();
            catController.CategoriesRepository = new MockCategoriesRepository(new List<IDAOCategories> {
            new DAOCategories() {CategoryID = 1, CategoryName = "Category One", Description = "First Category"},
            new DAOCategories() {CategoryID = 2, CategoryName = "Category Two", Description = "Second Category"},
            });

            var result = await catController.GetCategories(1) as OkNegotiatedContentResult<CategoriesVm>;
            Assert.IsNotNull(result);
            Assert.AreEqual("Category One", result.Content.CategoryName);
        }

        [TestMethod]
        public async Task GetCategory_ShouldNotFindCategory()
        {
            CategoriesController catController = new CategoriesController();
            catController.CategoriesRepository = new MockCategoriesRepository(new List<IDAOCategories>{
            new DAOCategories() {CategoryID = 1, CategoryName = "Category One", Description = "First Category"},
            new DAOCategories() {CategoryID = 2, CategoryName = "Category Two", Description = "Second Category"},
            });

            var result = await catController.GetCategories(5) as NotFoundResult;
            Assert.IsNotNull(result);
        }
    }
}

Running these tests gives you the following results, ie expected pass, and expected (and handled) fail.

pr_pr11a

 

Step 10. Add some Transaction tests. These tests will test that a couple of operations are submitted successfully as a single transaction, and if an error occurs, then all the operations are rolled back as a single transaction. To run these tests, we will be connecting to the database so we will be using a concrete repository rather than the mocked repository. First thing to do is to add an App.Config file to the Unit Test Project, with the appropriate connection string, you can copy this from the generated Web.config file in the Mvc project.

UnitTestProject1 – App.config



<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="CONNECTION-GUID-FROM-MVC-WEBCONFIG" value="Data Source=MYSERVER;Initial Catalog=Northwind; Connect Timeout=120;Integrated Security=SSPI;Persist Security Info=False;Packet Size=4096" />
</appSettings>
</configuration>

Next, Create a representative webapi controller action with a transactional operation we want to test. In the Mvc project, SampleApiControllers folder, add a class called CategoryProductsController.cs, with the following code:
CategoryProductsController.cs


using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Description;
using System.Threading.Tasks;
using TestableWebApiMvc.Mvc.SampleViewModels;
using TestableWebApiMvc.Business.Interfaces;
using TestableWebApiMvc.Business.Repository;
using TestableWebApiMvc.Business.Repository.Interfaces;

namespace TestableWebApiMvc.Mvc.SampleApiControllers
{
    public class CategoryProductsController : ApiController
    {
        /*get the relevant repositories from the repository factory*/
        private static RF _rf = RF.New();
        private IProductsRepository _productsRepo = _rf.ProductsRepository;
        private ICategoriesRepository _categoriesRepo = _rf.CategoriesRepository;

        [ResponseType(typeof(void))]
        public async Task<IHttpActionResult> PutCategoryProduct(CategoriesVm vm1, ProductsVm vm2)
        {
            if (!ModelState.IsValid) { return BadRequest(ModelState); }

            var result = Task.Factory.StartNew(() =>
            {
                IUnitOfWork uow = new UnitOfWorkImp(new IRepositoryConnection[] { _categoriesRepo, _productsRepo });
                /*get the domain objects from the viewmodel, and queue them up for transaction*/
                var boCategory = vm1.BOCategories(_categoriesRepo);
                uow.Update(boCategory);

                var boProduct = vm2.BOProducts(_productsRepo);
                uow.Update(boProduct);

                string err;
                /*commit transaction if all ok, rollback if not, and report error*/
                if (!uow.Commit(out err))
                {
                    var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(err) };
                    throw new HttpResponseException(resp);
                }
                return true;
            });
            await result;
            if (!result.Result)
                return NotFound();

            return StatusCode(HttpStatusCode.NoContent);
        }
    }
}

Next, expand the UnitTest1.cs class by adding the two new tests, plus a couple of support functions:


[TestMethod]
public async Task UpdateCategoryProduct_ShouldCommitTransaction()
{
    /**********************set up the test******************/
    RF _rf = RF.New();
    ICategoriesRepository categoriesRepository = _rf.CategoriesRepository;
    IProductsRepository productsRepository = _rf.ProductsRepository;

    //create a test category and product
    BOCategories testCategory = CreateTestCategory(categoriesRepository);
    BOProducts testProduct = CreateTestProduct(productsRepository, (int)testCategory.CategoryID);
    int testCategoryID = (int)testCategory.CategoryID;
    int testProductID = (int)testProduct.ProductID;
    /***********************end of test setup*****************/

    //run the test, update the category name, and product name
    testCategory.CategoryName = "Updated";
    testProduct.ProductName = "Updated";
    CategoryProductsController categoryProductsController = new CategoryProductsController();
    var result = await categoryProductsController.PutCategoryProduct(new CategoriesVm(testCategory), new ProductsVm(testProduct));

    //check the category name was updated in repository
    BOCategories checkCategory = new BOCategories() { Repository = categoriesRepository };
    checkCategory.Init(testCategoryID);
    StringAssert.Equals(checkCategory.CategoryName, "Updated");

    //check the product name was updated in repository
    BOProducts checkProduct = new BOProducts() { Repository = productsRepository };
    checkProduct.Init(testProductID);
    StringAssert.Equals(checkProduct.ProductName, "Updated");

    /**********tear down the test******************************************/
    testProduct.Delete();
    testCategory.Delete();
    /**********end of test tear down***************************************/
}


[TestMethod]
public async Task UpdateCategoryProduct_ShouldRollbackTransaction()
{
    /**********************set up the test******************/
    RF _rf = RF.New();
    ICategoriesRepository categoriesRepository = _rf.CategoriesRepository;
    IProductsRepository productsRepository = _rf.ProductsRepository;

    //create a test category and product
    BOCategories testCategory = CreateTestCategory(categoriesRepository);
    BOProducts testProduct = CreateTestProduct(productsRepository, (int)testCategory.CategoryID);
    int testCategoryID = (int)testCategory.CategoryID;
    /***********************end of test setup*****************/

    //run the test, update the category name, but also update the product with an invalid categoryid, 
    //so the transaction will be rolled back
    testCategory.CategoryName = "Updated";
    testProduct.CategoryID = 2000000; //invalid category id
    CategoryProductsController categoryProductsController = new CategoryProductsController();

    try
    {
        var result = await categoryProductsController.PutCategoryProduct(new CategoriesVm(testCategory), new ProductsVm(testProduct));
        Assert.Fail();//should not get here.
    }
    catch (System.Web.Http.HttpResponseException ex)
    {
        string errorMsg = ex.Response.Content.ReadAsStringAsync().Result;
        StringAssert.Contains(errorMsg, "FOREIGN KEY");

        //so we know the product update failed. Check the 'Updated' value of category has also been rolled back.
        BOCategories checkCategory = new BOCategories() { Repository = categoriesRepository };
        checkCategory.Init(testCategoryID);
        StringAssert.Equals(checkCategory.CategoryName, "Not Updated");
    }

    /**********tear down the test******************************************/
    testProduct.Delete();
    testCategory.Delete();
    /**********end of test tear down***************************************/
}

private BOCategories CreateTestCategory(ICategoriesRepository categoriesRepository)
{
    BOCategories testCategory = new BOCategories() { Repository = categoriesRepository };
    testCategory.CategoryName = "Not Updated";
    testCategory.Description = "Test Category";
    testCategory.SaveNew();
    return testCategory;
}

private BOProducts CreateTestProduct(IProductsRepository productsRepository, int validCategoryID)
{
    BOProducts testProduct = new BOProducts() { Repository = productsRepository };
    testProduct.ProductName = "New Test Product";
    testProduct.Discontinued = false;
    testProduct.CategoryID = validCategoryID;
    testProduct.SaveNew();
    return testProduct;
}

 

Finally, run the tests and confirm that one transaction is committed successfully, and the other is rolled back successfully.

pr_pr14a

 

Step 11. Check the time. All told, 15 mins? maybe 20? Never mind, time well spent and job well done.

 

Database to full asp.net Mvc App – in 5 minutes

Database to full asp.net Mvc App – in 5 minutes

So you are sitting in one of those interminable tuesday morning meetings at work, trying not to spill your now-cold coffee, wondering if the cryptic doodling on your pad suggests anything about your state of mind, when the project manager says ‘..and I’m sure Nick could convert the database into a fully functional Asp.Net Mvc application, so that we can manage the data in a more user friendly and secure manner. How long do you think that would take, Nick?’.

And now everyone turns to look at you.

Scrambling to figure out what the context is and how the conversation has turned to this, you pluck a hopefully reasonable figure out of the air. ‘Yeah, should be able to turn that around in a couple of weeks..’ you say brightly.

‘Excellent’ says the project manager, ‘I knew i could depend on you’. Everyone smiles in relief, and the meeting then proceeds to plan the complex multi-team delivery of downstream modules, all of it dependent on your proposed timescale.

As you all file out of meeting room 7c, the project manager says ‘..oh by the way, i believe its an Oracle database that we need converting to the Mvc app, and we’ll be needing it to use WebApi, and Asp.Net Identity for secure authentication etc, that’s not going to be a problem is it?’

‘Um..’ you say.

Back at your desk, you are wondering if anyone has ever undertaken to build an asp.net mvc app on an Oracle database, doesn’t sound very likely. How on earth are you going to get off the ground with this, and deploy in 2 weeks?

Let me show you how to do it in 5 minutes.

[You will need Visual Studio (2012+) and the CodeTrigger For Visual Studio extension (Full Trial, or Professional Version, v4.8.6.5+), Windows 7.1+, .Net Framework 4.6.1+. Once you got that setup, make a note of the time and begin]

Step 1. Start a new CodeTrigger project within Visual Studio, give it a name, and select the ‘DB to Multi-tier Mvc ..’ wizard. Click next.

pp1b-pr1

Step 2. Select your data source type, and configure the connection settings. Click ‘Connect’ to verify the settings, and click ‘Create’.

pp2a-pr2

pp11a-pr11

 

Step 3. Voila, the multi-tier Visual Studio project is generated and pre-built. (If you don’t have nuget automatic package download enabled, you may have missing package build errors at this stage, in this case, enable nuget automatic package download on your solution, or otherwise download the missing asp.net core packages from nuget, and rebuild. If you do have nuget automatic package download enabled then this is all done for you).

Note that this wizard will attempt to automatically create the identity tables required by Asp.Net identity, in your database. If those tables already exist then they will not be regenerated.

In the ‘Schema Objects’ tab, select any tables you want to be represented in the final application. If you have 15 tables or less, you might as well click the ‘Select all’ checkbox.

pp3b-pr3

Step 4. Click on the ‘Business Objects’ tab. The business model is generated from your previous selections and all related business model entities are displayed for your selection, along with their relationships. Again, if the number of model entities is about 15 or less, you may as well select all, otherwise choose judiciously according to your project needs.

pp4b-pr4

Step 5. Shoot! Click the red button to generate the selected code classes. Stand back and be happy as CodeTrigger spews out several thousand lines of required code in a matter of seconds! At this point the project/solution will build itself and report happy. If there’s any outstanding tasks, they might be reported in the ‘Things to do’ tab but I find that useful only when I’ve changed some of the default automatic code generation settings (like automatic sql scripting or automatic project file update).

pp5a-pr5

Step 6. Hit F5 to build and run. At this point (or soon after) when I’m building an Oracle based MVC app (as opposed to Sql Server or MySQL), I sometimes find that the solution fails with an unhandled Oracle client Exception. This seems to be because Oracle is still in shock about what just happened. If this occurs I close Visual Studio and re-open it, and that seems to cure the issue once and for all.

Enjoy the sight of your fully functioning Asp.Net Mvc App. Login using an auto generated login for debug purposes (Username: Admin@example.com Password: Admin@123456).

pp6a-pr6

pp6b-pr6

Once logged in, click on the ‘Sample Pages’ link to view/update your various entities.

pp10b-pr10

Step 7. Check the time. 5 minutes! Project manager is still writing up his minutes from the meeting, and you’re already done. What are you going to do with the remaining 13 days, 11 hours and 55 minutes? Well you can get a fresh hot coffee from Inca Beans (no, don’t warm up your old one in the microwave), and sit back. Now if only someone would write a tool that did all the unit, system and integration testing in 5 minutes, you could be home for lunch.

 

Edit 29th June 2016: For a version of this approach which enables unit-testing by using the mockable repository pattern, see Database to Testable WebApi MVC app in 15 mins