Build an MVC 6 app using Code Generation Only

This is an updated and expanded edition of a previous post series on StoutCloud.com. The idea is to use the latest nightly build ASP.NET 5 code and spin up an app using mostly code generators. This includes accessing an existing SQL Server database using Entity Framework 7 code generation as well as using MVC 6 code generators.

You will need to set up the .k directory to store the runtimes. If you setup Visual Studio 2015 CTP6 and create an ASP.NET 5 Web App you should wind up with a .k directory with packages and runtimes under it. I like having the bin folder as well which holds the kvm.cmd and kvm.ps1. This allows you to manage the installation of runtimes. Use this post for help getting the .k environment setup.
You should be at the point where you run

kvm list

and it looks like this.
kvmlistdotk

My environment is Windows 7 SP1, Visual Studio 2015 CTP 6, and SQL Server 2014 Express with latest updates (CU6). The folks at SQLSkills have allowed me to use their Credit training database for this blog series. It has ten tables with a good mix of column types and constraints.
Click here to download the Credit Database from SQLSkills (this will download file (53 MB)).

creditdatabase
Let’s build the app.
Create an C# ASP.NET 5 Preview Empty Web Application called TotallyGenWebApp using Visual Studio 2015.

aspnetempty

Go to Package Manager Console to install some packages.
Run the following commands with package source as aspnetvnext nightly build:
Install-Package -Pre EntityFramework.SqlServer
Install-Package -Pre EntityFramework.Commands
Install-Package -Pre EntityFramework.SqlServer.Design
Install-Package -Pre Microsoft.AspNet.Server.WebListener
Install-Package -Pre Microsoft.AspNet.Mvc
Install-Package -Pre Microsoft.Framework.CodeGenerators.Mvc

nugetinstall

I have added a path to the runtime bin directory and you may have done this already from the post I referenced. This is important when we run our k commands later that we are using the latest runtime.
addpathtoruntimebin

Let’s add some commands to our project.json file that we will be executing shortly.
Get your project.json file to look like this. Note I updated the version of package Microsoft.AspNet.Server.IIS to make consistent.

{
    "webroot": "wwwroot",
    "version": "1.0.0-*",
    "exclude": [
        "wwwroot"
    ],
    "packExclude": [
        "node_modules",
        "bower_components",
        "**.kproj",
        "**.user",
        "**.vspscc"
    ],
    "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta4-*",
        "EntityFramework.SqlServer": "7.0.0-beta4-*",
        "EntityFramework.Commands": "7.0.0-beta4-*",
        "EntityFramework.SqlServer.Design": "7.0.0-beta4-*",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4-*",
        "Microsoft.AspNet.Hosting": "1.0.0-beta4-*",
        "Microsoft.AspNet.Mvc": "6.0.0-beta4-*",
        "Microsoft.AspNet.Diagnostics": "1.0.0-beta4-*",
        "Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta4-*",
        "Remotion.Linq": "2.0.0-alpha-*"
    },
    "frameworks": {
        "aspnet50": { },
        "aspnetcore50": { }
    },
    "commands": {
		"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000",
		"gen": "Microsoft.Framework.CodeGeneration",
		"ef": "EntityFramework.Commands"
	}
}

Build your app making sure there are no build errors.

I added three top-level directories to hold EF7 generated code. You can do the same.
threetopleveldirectories

Open a command prompt window and go the the project directory where the project.json file resides. We will run all k commands from here.
Run kpm restore, this makes sure we have everything downloaded. Note you may need to run this more than once.
kpmrestore
I can quickly verify runtime and EF versions with the following commands and they match the latest from the nightly build.
k --version
k ef --version
Now enter the command k ef

efcommands

These are the Entity Framework commands available to us. Notice a new command available “revEng” which allows us to reverse engineer code from a database. As you can imagine this could be very useful and we will make use of this right now. Let’s reverse engineer the Credit database. I have this database on SQL Server 2014 Express on my machine.

Adjust the following command for your machine. The single argument after revEng is just a connection string for SQL Server. My machine name is Glenn-PC, SQL Server instance is named SQLEXPRESS and the database name is Credit.

k ef revEng Server=Glenn-PC\SQLEXPRESS;Database=Credit;Trusted_Connection=True;

After running the command you should see several C# source files added to your project. One for each entity (table) and one for the database context named after the database Credit.

Here is the code for the member entity.

    public partial class member
    {
        public member()
        {
            charge = new HashSet();
            payment = new HashSet();
            statement = new HashSet();
        }
        
        // Properties
        public int member_no { get; set; }
        public string city { get; set; }
        public string country { get; set; }
        public decimal? curr_balance { get; set; }
        public DateTime expr_dt { get; set; }
        public string firstname { get; set; }
        public DateTime issue_dt { get; set; }
        public string lastname { get; set; }
        public string mail_code { get; set; }
        public string member_code { get; set; }
        public string middleinitial { get; set; }
        public string phone_no { get; set; }
        public byte[] photograph { get; set; }
        public decimal? prev_balance { get; set; }
        public string state_prov { get; set; }
        public string street { get; set; }
        
        // Navigation Properties
        public virtual ICollection charge { get; set; }
        public virtual ICollection payment { get; set; }
        public virtual ICollection statement { get; set; }
        public virtual corporation corporation { get; set; }
        public virtual region region { get; set; }
    }

The properties are already configured for us by Fluent API in the context class Credit.

            modelBuilder.Entity<member>(entity =>
                {
                    entity.Key(e => e.member_no);
                    entity.Property(e => e.city)
                        .MaxLength(15);
                    entity.Property(e => e.country)
                        .MaxLength(2);
                    entity.Property(e => e.firstname)
                        .MaxLength(15);
                    entity.Property(e => e.lastname)
                        .MaxLength(15);
                    entity.Property(e => e.mail_code)
                        .MaxLength(10);
                    entity.Property(e => e.member_code)
                        .MaxLength(2);
                    entity.Property(e => e.middleinitial)
                        .MaxLength(1);
                    entity.Property(e => e.phone_no)
                        .MaxLength(13);
                    entity.Property(e => e.state_prov)
                        .MaxLength(2);
                    entity.Property(e => e.street)
                        .MaxLength(15);
                }
            );

And the entity navigation properties defining some of the relationships are also further configured for us with Fluent API in the context class.

 modelBuilder.Entity<member>(entity =>
                {
                    entity.HasOne(d => d.corporation).WithMany(p => p.member);
                    entity.HasOne(d => d.region).WithMany(p => p.member);
                }
            );

That is a lot of code generated for us.
Picture one day when we can do that with not only SQL Server but any type of datastore.

Let’s move the ten entity classes into the Models folder we created and the context class Credit into the DAL folder. I’m putting all the entity classes in namespace TotallyGenWebApp.Models and the Credit class into namespace TotallyGenWebApp.DAL. We also will need to add a using directive to the Credit source file,

using TotallyGenWebApp.Models;

Build the project making sure their are no compile errors.

At the command prompt enter the command
k gen -h
You should see a list of the code generators available.
Enter the command
k gen controller -h
We will be using the controller code generator and will target the region entity.
Let’s now create our controller and views with one command, go ahead and run this.
k gen controller -name Regions -m TotallyGenWebApp.Models.region -dc TotallyGenWebApp.DAL.Credit
We now have folders Controllers and Views in our project containing the generated code.
Here is the Regions controller code.

using System.Linq;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using TotallyGenWebApp.DAL;
using TotallyGenWebApp.Models;

namespace TotallyGenWebApp.Controllers
{
    public class Regions : Controller
    {
        private Credit db = new Credit();

        // GET: Regions
        public IActionResult Index()
        {
            return View(db.region.ToList());
        }

        // GET: Regions/Details/5
        public IActionResult Details(System.Int32? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(404);
            }

            region region = db.region.Single(m => m.region_no == id);
            if (region == null)
            {
                return new HttpStatusCodeResult(404);
            }

            return View(region);
        }

        // GET: Regions/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Regions/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(region region)
        {
            if (ModelState.IsValid)
            {
                db.region.Add(region);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(region);
        }

        // GET: Regions/Edit/5
        public IActionResult Edit(System.Int32? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(404);
            }

            region region = db.region.Single(m => m.region_no == id);
            if (region == null)
            {
                return new HttpStatusCodeResult(404);
            }

            return View(region);
        }

        // POST: Regions/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(region region)
        {
            if (ModelState.IsValid)
            {
                db.Update(region);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(region);
        }

        // GET: Regions/Delete/5
        [ActionName("Delete")]
        public IActionResult Delete(System.Int32? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(404);
            }

            region region = db.region.Single(m => m.region_no == id);
            if (region == null)
            {
                return new HttpStatusCodeResult(404);
            }

            return View(region);
        }

        // POST: Regions/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(System.Int32 id)
        {
            region region = db.region.Single(m => m.region_no == id);
            db.region.Remove(region);
            db.SaveChanges();

            return RedirectToAction("Index");
        }
    }
}

Notice the controller has methods generated for CRUD functionality when working with regions.
Five MVC views were generated for Regions along with two shared views as well.
You can also find content added under wwwroot now with supporting scripts and css.
I know , crazy right. We haven’t typed one line of code yet.
There is even a read me file ScaffoldingReadMe.txt that tells us what to put in our Startup class. Here is the content.

Add the following namespace usings if not already added:

using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;

Add the following code to the end of Configure method in your Application's Startup class if not already done:

            // Set up application services
            app.UseServices(services =>
            {
                // Add MVC services to the services container
                services.AddMvc();

            });

            // Add MVC to the request pipeline
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Home", action = "Index" });
            
                routes.MapRoute(
                    name: "api",
                    template: "{controller}/{id?}");
            });

            // Add static files to the request pipeline
            app.UseStaticFiles();

 

Follow the suggestions in this file but make the UseServices method call look like this which includes Entity Framework set up.

// Set up application services
            app.UseServices(services =>
            {
                services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<Credit>();
                // Add MVC services to the services container
                services.AddMvc();

            });

Also add

using TotallyGenWebApp.DAL;

to the top of Startup.cs.

Build your application making sure there are no build errors.
Let’s see what we have now from all this code generation.

At the same command line prompt enter the command
k web
You should see Started message returned.
The app is now hosted and running.
Start up a web brower and navigate to
http://localhost:5000/Regions

You should see the complete list of regions returned from the database.

regions

And that seems to match what I find in the Credit database when selecting data from the region table.

whatsinthedb

Not bad for almost no coding.