Site Meter
 
 

MvcScaffolding: Standard Usage

This post is part of a series about the MvcScaffolding NuGet package:

  1. Introduction: Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package
  2. This post: Standard usage: Typical use cases and options
  3. One-to-Many Relationships
  4. Scaffolding Actions and Unit Tests
  5. Overriding the T4 templates
  6. Creating custom scaffolders
  7. Scaffolding custom collections of files

This post describes some of the basic functionality you get with the MvcScaffolding package as soon as you install it. First, though, let’s explain a few core concepts. Don’t worry, there aren’t too many strange new concepts…

If this is the first time you’ve heard about MvcScaffolding, check out this other introductory post first.

What is a scaffolder?

A scaffolder is something that does scaffolding :) . Typically it consists of a PowerShell script that holds its logic, and a set of T4 templates that define the output it can produce, though you don’t need to know about how they work unless you want to make your own.

To see a list of available scaffolders, issue this command in the Package Manager Console:

Get-Scaffolder

image

Hopefully, your screen will be a little wider and you’ll actually be able to read the descriptions! Anyway, as you can see, scaffolders can live in NuGet packages and they have unique names like MvcScaffolding.RazorView. You could have lots of different scaffolders that produce views (e.g., you already have MvcScaffolding.AspxView too), and identify them by their unique name. For example, you could choose to scaffold an ASPX view as follows:

Scaffold MvcScaffolding.AspxView Index Team

Here, “Team” is the name of my model class (see the previous post for the steps to set up the project). Your model class might be Product or Customer or whatever. You can enter a fully-qualified type name, or just the unqualified name, and the scaffolder will search your referenced projects to find it.

Scaffolders can accept their own set of parameters.  In PowerShell, named parameters are introduced using a dash (“-“) characters, so to see a list of available parameters, enter text as far as the dash and then press TAB:

image

Some of those are specific to the scaffolder; others are common PowerShell parameters. I’ll explain what the scaffolder-specific ones do later.

What are “default scaffolders”?

You don’t want to keep typing out those long scaffolder names every time you want to use them (even with the tab-completion). Maybe you always want to use MvcScaffolding.RazorView by default for all views. Well, that’s exactly what defaults do.

To see what scaffolders are configured as defaults, enter the following command:

Get-DefaultScaffolder

image

As shown in the screenshot, default names are short, generic identifiers like Controller or Repository or View. That’s what makes it possible to enter “Scaffold Controller …” or “Scaffold View …” and not have to enter the full scaffolder name.

Interesting aside: when you first installed MvcScaffolding, it inspected your project and picked a default “View” scaffolder based on which view engine you appear to be using.

Default names are mapped onto full scaffolder names by means of an XML configuration file. You don’t need to see or edit the configuration file, though, because you can read it using Get-DefaultScaffolder, and write it using Set-DefaultScaffolder.

For example, to say that you want to generate ASPX views by default, enter the following:

Set-DefaultScaffolder View MvcScaffolding.AspxView

Now if you re-run Get-DefaultScaffolder, you’ll see that the name View resolves to MvcScaffolding.AspxView. Also, if you enter “Get-Scaffolder View”, you’ll see it returns information about the MvcScaffolding.AspxView scaffolder.

image

Note that when scaffolders call each other internally (e.g., the MvcScaffolding.Controller scaffolder internally calls Scaffold Repository … and Scaffold View …), they do so using the default names, not the full names. That means if you’ve configured some other default for View, it will get used by the Controller scaffolder automatically.

These defaults are stored on a per-project basis (so you can have different defaults for different projects), but you can also define solution-wide defaults by passing the “-SolutionWide” flag to Set-DefaultScaffolder.

Using the built-in scaffolders

Here’s what you can do with MvcScaffolding (and its parent package, T4Scaffolding) automatically:

Scaffold Controller <controllerNameOrModelType>

This produces an ASP.NET MVC controller for CRUD operations with an EF-code-first data context and views for Create, Details, Delete, Edit, and Index. Options include:

  • -ControllerName : Specify the name of the controller you want. If you’ve entered something that looks like a controller name, e.g., ProductsController or ProductController, we’ll use that name and go looking for a model class called Products or Product. If you enter something that looks like a model type, e.g., Product, we’ll look for that model and create you a controller called ProductsController.
  • -ModelType : By default we try to infer this from the controller name, but if you want your controllers and models to have totally unrelated names, you can specify a particular model type using this parameter. We’ll find any model class defined in your project or another project it references, but not in any external assembly.
  • -Project : Specify which ASP.NET MVC project the output goes into (you could have multiple such projects in your solution – by default we use the one specified by the “Default project” dropdown at the top of the Package Manager Console).
  • -CodeLanguage : Specify either “cs” or “vb” if you want. By default we use the same code language as your project. Theoretically you could pass “fsharp” or any other string for this parameter, but it would only work if the scaffolder could find a matching template.
  • -DbContextType : Specify the name of the database context class that should be generated or updated. By default, we use the name <yourProjectName>Context, e.g., SoccerSiteContext.
  • -Repository : This is a switch parameter, which means it doesn’t take any value. Just adding –Repository to the command line means that we’ll generate a repository class and interface, and the controller will do data access using that repository interface.
  • -Area : If you want the generated files to go into a specific ASP.NET MVC area, enter its name here. Note that you must have created that area already; the scaffolder doesn’t create areas automatically (yet).
  • -Layout : (Note: –MasterPage is a PowerShell alias for this parameter, so you can enter either name) If you want to use a specific Razor layout or ASPX Master Page, enter its virtual path here, e.g., “-Layout /Views/Shared/_SomeLayout.cshtml”. Otherwise, if you’re using Razor we’ll assume you want the default layout, and if you’re using ASPX we’ll look for a default Master Page (~/Views/Shared/Site.Master) or if not found produce a full HTML page (i.e., no master page).
  • -Force : By default, we don’t overwrite any files that already exist. If you do want to overwrite things, pass this switch.
  • -NoChildItems : If for some reason you only want to generate the controller class and don’t want any views, repositories, or data contexts, pass this switch.

In due course, we’ll add proper PowerShell-based documentation so you can get help on any specific scaffolder or parameter directly from the Package Manager Console.

Scaffold View <controllerName> <viewName>

This produces an empty view associated with the specified controller. Note that you should provide the short name for the controller, i.e., Products not ProductsController, as this is the name of the folder where the view goes. Also, you can specify:

  • -Template : Should be one of Create, Delete, Details, Edit, Index, or Edit. Instead of producing an empty view, we’ll use the specified template
  • -ModelType : Creates a strongly-typed view for the chosen model type. If you’re scaffolding a Create, Delete, Details, Edit, or Index view, the output will take account of the properties exposed by your model type.

Other parameters include: –Project, –Area, –Layout, –CodeLanguage, –Force. See above for information about these.

Scaffold Views <modelType>

This produces all of the standard CRUD views: Create, Delete, Details, Edit, and Index. Internally, the “controller” scaffolder calls this unless you’ve passed the –NoChildItems switch. It takes the same parameters as the “view” scaffolder.

Scaffold DbContext <modelType> <dbContextName>

This produces an Entity Framework Code First database context class for the given model type. If there’s already a database context class with matching name, we’ll simply add another DbSet<modelType>property to it without losing its existing properties. So, as you scaffold more models, your context will build up references to all the model types you’re persisting.

Other available parameters include:

  • -Folder : Forces the output to go into a specific folder relative to your project. By default, we use the /Models folder.
  • -Area : If you haven’t set a value for –Folder, this parameter populates –Folder using the ASP.NET MVC convention /Areas/<areaName>/Models.
  • Common ones such as –Project, –Force, and so on.
Scaffold Repository <modelType>

This produces a repository interface named after your model type (e.g., IProductRepository) along with an implementation that works using Entity Framework Code First. The repository exposes methods that get a specific model instance by primary key value, gets all model instances, adds a new model instance, and deletes a model instance. These operations don’t write to the database immediately – there’s also a “Save()” method which writes all the changes you made to the database.

Other parameters include:

  • -NoChildItems : Internally, the default repository scaffolder also calls “Scaffold DbContext …” to generate or update an underlying database context, which is what connects your application to the actual data access technology. If you don’t want to generate or update any database context, pass the –NoChildItems flag.
  • -Folder and –Area : These are passed on to the DbContext scaffolder (unless you pass the –NoChildItems flag, in which case the DbContext scaffolder won’t be called).
  • Common ones such as –Project, –Force, and so on.

Overriding the standard scaffolders with LINQ to SQL ones

To prove that this is an extensible system, I’ve created an experimental “LinqToSqlScaffolding” package that can replace the usual Entity Framework-powered “Repository” and “DbContext” scaffolders. To install it, enter the following into Package Manager Console:

Install-Package LinqToSqlScaffolding

Now if you run Get-Scaffolder, you’ll see there are two new ones:

image

If you want, you can invoke these directly (e.g., Scaffold LinqToSqlScaffolding.Repository <modelType>). They take roughly the same parameters as the standard EF repository and DbContext scaffolders.

But if you want to use LINQ to SQL by default, just update your default settings:

Set-DefaultScaffolder Repository LinqToSqlScaffolding.Repository

Now, whenever you call “Scaffold Repository” or even “Scaffold Controller”, it will generate code that works with LINQ to SQL. Note that to get this actually talking to a database successfully, you’ll need to:

  • Annotate your model classes with LINQ to SQL mapping attributes as described here, or provide a LINQ to SQL mapping file
  • Add a connection string called AppDb to your Web.config file, and ensure there’s really a database listening for connections, and set up the database schema to match your model.

Also, whenever you generate a controller for this, be sure to pass the –Repository switch because otherwise the controller will contain inline Entity Framework code.

Note that LINQ to SQL scaffolding support is basic, to say the least, and is primarily here to demonstrate how you can create a custom scaffolding package. In due course I hope others will create scaffolding packages to support other data access technologies, such as NHibernate. If you want the fame and glory and decide to be the one who builds that package for the community, let me know!

What’s next?

There’s still plenty more to cover about scaffolding. In the next post, I’ll describe how you can customise the T4 templates that define scaffolders’ output, and how you can create entirely new scaffolders of your own.

Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package

This post is part of a series about the MvcScaffolding NuGet package:

  1. This post: Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package
  2. Standard usage: Typical use cases and options
  3. One-to-Many Relationships
  4. Scaffolding Actions and Unit Tests
  5. Overriding the T4 templates
  6. Creating custom scaffolders
  7. Scaffolding custom collections of files

Among many other improvements with ASP.NET MVC 3, we’re keen to make it easier to pick up and start using productively if you’re entirely new to the framework, and to automate common development tasks if you’re experienced and already know what you’re doing.

So, I’ve been working with Scott Hanselman lately on an enhanced new scaffolding package called MvcScaffolding. The term “Scaffolding” is used by many software technologies to mean “quickly generating a basic outline of your software that you can then edit and customise”. The scaffolding package we’re creating for ASP.NET MVC is greatly beneficial in several scenarios:

  • If you’re learning ASP.NET MVC for the first time, because it gives you a fast way to get some useful, working code, that you can then edit and adapt according to your needs. It saves you from the trauma of looking at a blank page and having no idea where to start!
  • If you know ASP.NET MVC well and are now exploring some new add-on technology such as an object-relational mapper, a view engine, a testing library, etc., because the creator of that technology may have also created a scaffolding package for it.
  • If your work involves repeatedly creating similar classes or files of some sort, because you can create custom scaffolders that output test fixtures, deployment scripts, or whatever else you need. Everyone on your team can use your custom scaffolders, too.

Other features in MvcScaffolding include:

  • Support for C# and VB projects
  • Support for the Razor and ASPX view engines
  • Supports scaffolding into ASP.NET MVC areas and using custom view layouts/masters
  • You can easily customize the output by editing T4 templates
  • You can add entirely new scaffolders using custom PowerShell logic and custom T4 templates. These (and any custom parameters you’ve given them) automatically appear in the console tab-completion list.
  • You can get NuGet packages containing additional scaffolders for different technologies (e.g., there’s a proof-of-concept one for LINQ to SQL now) and mix and match them together

It’s currently at a beta level so it is expected to work :) but if you push it in funny ways you might get odd results. Please let me know if you discover issues – I’m sure there’ll be some – so we can tidy it up and make it more robust.

Installation

The short version is this: Install-Package MvcScaffolding. If you understood that, do it and skip ahead to “Scaffolding a CRUD interface”. If not, read on.

1. Install ASP.NET MVC 3, which includes the excellent NuGet Package Manager.

2. Create or open an ASP.NET MVC 3 web application. I’m calling mine ‘SoccerSite’.

3. Install the MvcScaffolding package. You can install it using the NuGet Package Manager Console, so it only takes a few seconds and you don’t have to download anything using your browser.  To do so,

  • Open the Package Manager Console window using Visual Studio’s View->Other Windows->Package Manager Console menu item.
  • Enter the following:
Install-Package MvcScaffolding

image

  • That’s it! Note: be sure you installed MvcScaffolding, not MvcScaffold. We’ll retire the old MvcScaffold package shortly.

In case you’re wondering, EFCodeFirst is the new super-elegant version of Entity Framework that persists plain .NET objects to a relational database without any configuration fuss, T4Scaffolding is the core scaffolding infrastructure (locates types in your project, finds scaffolders in other packages, renders templates, etc), and MvcScaffolding is a set of ASP.NET MVC-specific templates (for controllers and views) that bolts onto the T4Scaffolding core.

Scaffolding a CRUD interface

Let’s create a model for a soccer team. Add the following class to your Models folder, then compile your solution (Ctrl-Shift-B):

namespace SoccerSite.Models
{
    public class Team
    {
        public int TeamId { get; set; }
        [Required] public string Name { get; set; }
        public string City { get; set; }
        public DateTime Founded { get; set; }
    }
}

Next, we can create a complete Create-Read-Update-Delete (CRUD) UI for this model by issuing a single scaffolding command into the Package Manager Console:

Scaffold Controller Team

Note: You can use the TAB key to autocomplete many things in the Package Manager Console. In this case, you can autocomplete the words “Scaffold” and “Controller”, since the scaffolding package knows about them. It doesn’t know about “Team”, though – we may add completion on model type names in a future version.

image

As you can see, it’s gone ahead and created a controller, a database context (a tiny bit of Entity Framework code that represents a data store), and views for all the CRUD actions. You can run it right now (Shift-F5), and as long as you have SQL Server Express running on your machine, EFCodeFirst will automatically connect to it, create your database schema, and you’ve got a basic working application without writing a single line of procedural code.

Note that since the model was called “Team”, the controller is called “TeamController”, so to reach it you need to point your browser to http://…/team:

image

The database is initially empty.

image

Creating an item. Validation rules are applied automatically. Note that since “Founded” is a DateTime it can’t be null and hence is required. Change it to DateTime? (i.e., with the question mark to make it nullable) if you want it to be optional.

image

Listing items

image

Deleting an item

But what if I don’t have SQL Express installed?

If you don’t have SQL Express installed and running, you may have got the following error when your code tried to read or write some data:

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Blah blah blah…

No problem! You can quickly switch to use the new SQL Server Compact – a lightweight, in-process database – without having to download or install anything manually. Simply add SQL Server Compact to your project by issuing the following command in the Package Manager Console:

Install-Package EFCodeFirst.SqlServerCompact

Ta da – no more external database required. Run your project again (Shift-F5) and this time it will create and connect to a file-based database (a .sdf file will appear in your ~/App_Data folder). The EFCodeFirst.SqlServerCompact package adds a file to your project called AppStart_SQLCEEntityFramework.cs, which configures the runtime to use SQL CE.

Of course you probably still want the proper version of SQL Server when you eventually deploy your application for public use, but for small applications or for learning, SQL CE is really handy.

Scaffolding a repository

If you check out the code right now, you’ll see that TeamController reads and writes the data in SoccerSiteContext directly. That’s fine in many simple scenarios, but if you want to decouple your controller logic from persistence logic a little (e.g., so that you can write clean unit tests for the controller), you may prefer to reach your data through an interface.

No problem! Let’s regenerate the controller with the –Repository flag:

Scaffold Controller Team –Repository -Force

Notice that we also need to say –Force, otherwise the scaffolder won’t overwrite the files you already have in your project. Now the scaffolder will produce an additional class, TeamRepository, and the following interface which TeamRepository implements:

    public interface ITeamRepository
    {
        void Add(Team post);
        void Delete(int id);
        IEnumerable<team> GetAllTeams();
        Team GetById(int id);
        void Save();
    }

TeamController will now only read and write data using ITeamRepository. If you’re new to ASP.NET MVC it may not be obvious why this is desirable, but if you start trying to write unit tests or switch data access technologies, you’ll find this interface-based data access method to be much cleaner and more flexible.

There’s so much more

It’s not just about CRUD! You can use scaffolding to create any type of project item if you write a template for it. This blog post has covered only the absolute beginning of what you can do, so over the coming days I’ll write blog posts to cover:

  • Scaffolding specific individual items (e.g., views, repositories, etc) rather than whole controllers and related files
  • Getting additional scaffolder packages and controlling which ones are used by default. For example, there’s currently a proof-of-concept LINQ to SQL scaffolding package that you can install and set to act as the default type of repository/data context.
  • Customising the T4 templates that the scaffolders use to generate code
  • Creating entirely new custom scaffolders for new types of things (e.g., unit test fixtures)

Using 51Degrees.Mobi Foundation for accurate mobile browser detection on ASP.NET MVC 3

Mobile devices continue to grow in popularity as a way to access the Web. If you’re building any kind of public-facing site, you probably way to make it look and behave well on mobiles just as on traditional desktop browsers.

ASP.NET MVC allows for lots of possible architectures to support this:

  1. You could render different views for mobiles, as in Scott Hanselman’s recent post
  2. You could have a separate MVC area for mobiles, so you can handle their requests using different controller logic as well as different views
  3. You could simply have a separate layout/master page for mobiles, but otherwise let them share the same controller logic and views as desktop browsers

Hopefully soon we’ll publish some updated and more detailed guidance about these options, but for now this blog post is about option (3) – the simplest of them all.

Setting up a mobile-specific Razor layout

One of the really neat aspects of the Razor view engine (the default in ASP.NET MVC 3) is that a lot of it can be controlled by procedural code, not just declarative statements. So, if you want to implement custom logic to control layout selection, just add some logic to ~/Views/_ViewStart.cshtml. For example, change this:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

… to this:

@{
    Layout = Request.Browser.IsMobileDevice
        ? "~/Views/Shared/_LayoutMobile.cshtml"
        : "~/Views/Shared/_Layout.cshtml";
}

… and now it will attempt to use _LayoutMobile.cshtml for requests from mobiles. Next, of course, you’ll need to actually create _LayoutMobile.cshtml. To get started, you can just copy & paste the existing _Layout.chtml file, changing its name. To ensure we can tell the difference between the two, I’ll modify the “title” element to display a special message for mobiles:

<div id="title">
    <h1>My *mobile* app</h1>
</div>

Time to check it works. Perhaps the quickest way to simulate a mobile device (without downloading any emulators) is by using Firefox and its User Agent Switcher add-on: it already knows how to simulate the iPhone’s user agent string, for example. Here’s how my home page renders, first with the default Firefox user agent string, and second simulating the iPhone’s user agent string:

image

Spot the difference :)

At the point, you might think we’re done. Surely from here it’s just a matter of customizing _LayoutMobile.cshtml to better suit the small screen of a mobile device, right? Well, kind of. But how does Request.Browser.IsMobileDevice actually work? Is it always right?

Some popular mobile devices/browsers won’t be detected this way

In case you don’t know, Request.Browser is a core ASP.NET platform feature that can give you a lot of information about the type and capabilities of the browser making the current request. It works by comparing the incoming User-Agent header against regular expressions in a set of built-in .browser files. ASP.NET 4’s browser files are recent enough to detect iPhone, but not Opera Mobile or Android. For example, if you download the Opera Mobile emulator and visit the page we just created, here’s what you’ll see:

image

If you can read the tiny text, you’ll see it doesn’t say “My *mobile* app”.

One way to improve this is to add your own .browser files for newer devices and browsers – accounting for the many variations in their user agent strings. Then Request.Browser.IsMobileDevice could successfully recognize Opera Mobile. But if you don’t want to do all that work?

Improving device detection using the 51Degrees.Mobi package

51Degrees.Mobi Foundation is an open source .NET project that enhances Request.Browser so it gets its information from Wireless Universal Resource File (WURFL) – one of the most comprehensive and up-to-date databases of mobile device information. The great news is that 51Degrees.Mobi Foundation is now available as a NuGet package, so it’s incredibly easy to install and update.

If you haven’t already installed the NuGet package manager, do so right away! Next, install 51Degrees by entering the following into the NuGet Package Manager Console:

Install-Package 51Degrees.mobi

Easy! This does the following to your project:

  • Adds a reference to FiftyOne.Foundation.dll, the assembly containing the 51Degrees logic
  • Adds AppStart_51Degrees.cs to the root of your project. This enables the 51Degrees mobile detection provider, which populates Request.Browser information whenever you ask for it
  • Adds a recent copy of the WURFL device list to your project at ~/App_Data/wurfl.xml.gz
  • Adds a folder, ~/Mobile/ containing a Web Form that displays information about the device. We don’t want this in an ASP.NET MVC application – just delete the ~/Mobile folder that it added.
  • Adds a section to your Web.config file that configures 51Degrees.Mobi Foundation with some default settings. One of its default settings is to redirect mobile browsers to the URL ~/Mobile. This would be useful if you were setting up an MVC Area called “Mobile” for mobile browsers, but since we’re just changing the master page, we don’t want this redirection to happen. Disable it by modifying Web.config: Find the <redirect> element inside <fiftyOne>, and comment it out or delete it.

Now you’ve got a much more up-to-date device database. Here’s what you’ll see if you reload the site in Opera Mobile emulator:

 image

You can download newer versions of the WURFL database to pick up newly-released mobile devices and browsers at any time from its page on SourceForge. Whenever you download a new version, install it by copying it to the location ~/App_Data/wurfl.xml.gz, overwriting your previous version of that file. Note that WURFL does not yet appear to detect Windows Phone 7 WURFL does detect Windows Phone 7 already.

Improving the layout for small screens

OK, we’ve got as far as distinguishing mobiles from desktop browsers and rendering views with different Razor layout file. It’s beyond the scope of this blog post to explain all the dozens of HTML and CSS tricks you can use to create layouts that look great on the widest range of mobiles, but here’s one I want to mention:

When modern mobile browsers render web pages, they lay it out on a virtual “viewport” that by default is about as wide as a typical desktop browser window. Then, after rendering the page, they scale down the output to fit on the tiny device screen. That’s why the preceding Opera Mobile screenshots have really tiny body text: the HTML page was laid out for a screen around 1000px wide and then scaled down. The user is expected to zoom in and pan around to get a closer look at the text.

Mobiles do this as a workaround for the fact that most web pages assume you’re using a desktop browser. If the mobile didn’t do this, typical web pages wouldn’t fit horizontally and would end up getting hideously mangled. But if you’re designing a layout especially for mobiles, you’ll take account of the much smaller width and spare the user all that awkward zooming and panning.

How can you tell the mobile not to use a wide virtual viewport, but instead to lay out the page to match the device’s actual (narrow) width? You can use the unofficial “viewport” meta tag. Here’s an example. Add the following to the <head> section of _LayoutMobile.cshtml:

<meta name="viewport" content="width=device-width">

Now if you reload the page in Opera Mobile emulator, here’s what you’ll see:

image

Much better! The body text is now pretty much readable, and the user only needs to scroll vertically (not horizontally). No zooming required. The “viewport” meta tag is respected by most recent mobile devices/browsers, including Windows Phone 7 and iPhone. Read more about how iPhone interprets it here.

From here on, it’s up to you to customize your mobile layout, perhaps using a different CSS stylesheet, to look as neat as possible on mobiles. Of course, you can also inspect Request.Browser.IsMobileDevice in other parts of your code too (e.g., controllers or filters) if you need to vary your logic further.