August 2012 - Posts - Jon Galloway

August 2012 - Posts

SimpleMembership, Membership Providers, Universal Providers and the new ASP.NET 4.5 Web Forms and ASP.NET MVC 4 templates

The ASP.NET MVC 4 Internet template adds some new, very useful features which are built on top of SimpleMembership. These changes add some great features, like a much simpler and extensible membership API and support for OAuth. However, the new account management features require SimpleMembership and won't work against existing ASP.NET Membership Providers. I'll start with a summary of top things you need to know, then dig into a lot more detail.

Summary:

  • SimpleMembership has been designed as a replacement for the previous ASP.NET Role and Membership provider system
  • SimpleMembership solves common problems developers ran into with the Membership provider system and was designed for modern user / membership / storage needs
  • SimpleMembership integrates with the previous membership system, but you can't use a MembershipProvider with SimpleMembership
  • The new ASP.NET MVC 4 Internet application template AccountController requires SimpleMembership and is not compatible with previous MembershipProviders
  • You can continue to use existing ASP.NET Role and Membership providers in ASP.NET 4.5 and ASP.NET MVC 4 - just not with the ASP.NET MVC 4 AccountController
  • The existing ASP.NET Role and Membership provider system remains supported, as it is part of the ASP.NET core
  • ASP.NET 4.5 Web Forms does not use SimpleMembership; it implements OAuth on top of ASP.NET Membership
  • The ASP.NET Web Site Administration Tool (WSAT) is not compatible with SimpleMembership

The following is the result of a few conversations with Erik Porter (PM for ASP.NET MVC) to make sure I had some the overall details straight, combined with a lot of time digging around in ILSpy and Visual Studio's assembly browsing tools.

SimpleMembership: The future of membership for ASP.NET

The ASP.NET Membership system was introduced with ASP.NET 2.0 back in 2005. It was designed to solve common site membership requirements at the time, which generally involved username / password based registration and profile storage in SQL Server. It was designed with a few extensibility mechanisms - notably a provider system (which allowed you override some specifics like backing storage) and the ability to store additional profile information (although the additional  profile information was packed into a single column which usually required access through the API). While it's sometimes frustrating to work with, it's held up for seven years - probably since it handles the main use case (username / password based membership in a SQL Server database) smoothly and can be adapted to most other needs (again, often frustrating, but it can work).

The ASP.NET Web Pages and WebMatrix efforts allowed the team an opportunity to take a new look at a lot of things - e.g. the Razor syntax started with ASP.NET Web Pages, not ASP.NET MVC. The ASP.NET Web Pages team designed SimpleMembership to (wait for it) simplify the task of dealing with membership. As Matthew Osborn said in his post Using SimpleMembership With ASP.NET WebPages:

With the introduction of ASP.NET WebPages and the WebMatrix stack our team has really be focusing on making things simpler for the developer. Based on a lot of customer feedback one of the areas that we wanted to improve was the built in security in ASP.NET. So with this release we took that time to create a new built in (and default for ASP.NET WebPages) security provider. I say provider because the new stuff is still built on the existing ASP.NET framework. So what do we call this new hotness that we have created? Well, none other than SimpleMembership. SimpleMembership is an umbrella term for both SimpleMembership and SimpleRoles.

Part of simplifying membership involved fixing some common problems with ASP.NET Membership.

Problems with ASP.NET Membership

ASP.NET Membership was very obviously designed around a set of assumptions:

  • Users and user information would most likely be stored in a full SQL Server database or in Active Directory
  • User and profile information would be optimized around a set of common attributes (UserName, Password, IsApproved, CreationDate, Comment, Role membership...) and other user profile information would be accessed through a profile provider

Some problems fall out of these assumptions.

Requires Full SQL Server for default cases

The default, and most fully featured providers ASP.NET Membership providers (SQL Membership Provider, SQL Role Provider, SQL Profile Provider) require full SQL Server. They depend on stored procedure support, and they rely on SQL Server cache dependencies, they depend on agents for clean up and maintenance. So the main SQL Server based providers don't work well on SQL Server CE, won't work out of the box on SQL Azure, etc.

Note: Cory Fowler recently let me know about these Updated ASP.net scripts for use with Microsoft SQL Azure which do support membership, personalization, profile, and roles. But the fact that we need a support page with a set of separate SQL scripts underscores the underlying problem.

Aha, you say! Jon's forgetting the Universal Providers, a.k.a. System.Web.Providers! Hold on a bit, we'll get to those...

Custom Membership Providers have to work with a SQL-Server-centric API

If you want to work with another database or other membership storage system, you need to to inherit from the provider base classes and override a bunch of methods which are tightly focused on storing a MembershipUser in a relational database. It can be done (and you can often find pretty good ones that have already been written), but it's a good amount of work and often leaves you with ugly code that has a bunch of System.NotImplementedException fun since there are a lot of methods that just don't apply.

Designed around a specific view of users, roles and profiles

The existing providers are focused on traditional membership - a user has a username and a password, some specific roles on the site (e.g. administrator, premium user), and may have some additional "nice to have" optional information that can be accessed via an API in your application.

This doesn't fit well with some modern usage patterns:

  • In OAuth and OpenID, the user doesn't have a password
  • Often these kinds of scenarios map better to user claims or rights instead of monolithic user roles
  • For many sites, profile or other non-traditional information is very important and needs to come from somewhere other than an API call that maps to a database blob

What would work a lot better here is a system in which you were able to define your users, rights, and other attributes however you wanted and the membership system worked with your model - not the other way around.

Requires specific schema, overflow in blob columns

I've already mentioned this a few times, but it bears calling out separately - ASP.NET Membership focuses on SQL Server storage, and that storage is based on a very specific database schema.

aspnet_tutorial04_MembershipSetup_vb_figure10[1]

Update: This schema has been improved a lot with Universal Providers. The views and stored procedures have been removed, and the tables are simplified.

2012-09-05_16h12_30

Still, the main issues are unchanged: you're not in control of the schema, and any profile data is stored in property value blobs in the Profiles table:

2012-09-05_16h14_31

SimpleMembership as a better membership system

As you might have guessed, SimpleMembership was designed to address the above problems.

Works with your Schema

As Matthew Osborn explains in his Using SimpleMembership With ASP.NET WebPages post, SimpleMembership is designed to integrate with your database schema:

All SimpleMembership requires is that there are two columns on your users table so that we can hook up to it – an “ID” column and a “username” column. The important part here is that they can be named whatever you want. For instance username doesn't have to be an alias it could be an email column you just have to tell SimpleMembership to treat that as the “username” used to log in.

Matthew's example shows using a very simple user table named Users (it could be named anything) with a UserID and Username column, then a bunch of other columns he wanted in his app.

UsersTable[1]

Then we point SimpleMemberhip at that table with a one-liner:

WebSecurity.InitializeDatabaseFile("SecurityDemo.sdf", "Users", "UserID", "Username", true);

No other tables are needed, the table can be named anything we want, and can have pretty much any schema we want as long as we've got an ID and something that we can map to a username.

Broaden database support to the whole SQL Server family

While SimpleMembership is not database agnostic, it works across the SQL Server family. It continues to support full SQL Server, but it also works with SQL Azure, SQL Server CE, SQL Server Express, and LocalDB. Everything's implemented as SQL calls rather than requiring stored procedures, views, agents, and change notifications.

Note that SimpleMembership still requires some flavor of SQL Server - it won't work with MySQL, NoSQL databases, etc. You can take a look at the code in WebMatrix.WebData.dll using a tool like ILSpy if you'd like to see why - there are places where SQL Server specific SQL statements are being executed, especially when creating and initializing tables. It seems like you might be able to work with another database if you created the tables separately, but I haven't tried it and it's not supported at this point.

Note: I'm thinking it would be possible for SimpleMembership (or something compatible) to run Entity Framework so it would work with any database EF supports. That seems useful to me - thoughts?

Note: SimpleMembership has the same database support - anything in the SQL Server family - that Universal Providers brings to the ASP.NET Membership system.

UPDATE: Newer updates of Universal Providers - I believe starting with the 1.2 release on 8/16 - are now really database agnostic, so they'll work on any database that has an Entity Framework provider.

Easy to with Entity Framework Code First

The problem with with ASP.NET Membership's system for storing additional account information is that it's the gate keeper. That means you're stuck with its schema and accessing profile information through its API.

SimpleMembership flips that around by allowing you to use any table as a user store. That means you're in control of the user profile information, and you can access it however you'd like - it's just data. Let's look at a practical based on the AccountModel.cs class in an ASP.NET MVC 4 Internet project. Here I'm adding a Birthday property to the UserProfile class.

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int      UserId { get; set; }
    public string   UserName { get; set; }
    public DateTime Birthday { get; set; }
}

Now if I want to access that information, I can just grab the account by username and read the value.

var context = new UsersContext();
var username = User.Identity.Name;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
var birthday = user.Birthday;

So instead of thinking of SimpleMembership as a big membership API, think of it as something that handles membership based on your user database. In SimpleMembership, everything's keyed off a user row in a table you define rather than a bunch of entries in membership tables that were out of your control.

How SimpleMembership integrates with ASP.NET Membership

Okay, enough sales pitch (and hopefully background) on why things have changed. How does this affect you? Let's start with a diagram to show the relationship (note: I've simplified by removing a few classes to show the important relationships):

2012-08-29_16h59_20

So SimpleMembershipProvider is an implementaiton of an ExtendedMembershipProvider, which inherits from MembershipProvider and adds some other account / OAuth related things. Here's what ExtendedMembershipProvider adds to MembershipProvider:

ExtendedMembershipProvider

The important thing to take away here is that a SimpleMembershipProvider is a MembershipProvider, but a MembershipProvider is not a SimpleMembershipProvider.

This distinction is important in practice: you cannot use an existing MembershipProvider (including the Universal Providers found in System.Web.Providers) with an API that requires a SimpleMembershipProvider, including any of the calls in WebMatrix.WebData.WebSecurity or Microsoft.Web.WebPages.OAuth.OAuthWebSecurity.

However, that's as far as it goes. Membership Providers still work if you're accessing them through the standard Membership API, and all of the core stuff  - including the AuthorizeAttribute, role enforcement, etc. - will work just fine and without any change.

Let's look at how that affects you in terms of the new templates.

Membership in the ASP.NET MVC 4 project templates

ASP.NET MVC 4 offers six Project Templates:

  • Empty - Really empty, just the assemblies, folder structure and a tiny bit of basic configuration.
  • Basic - Like Empty, but with a bit of UI preconfigured (css / images / bundling).
  • Internet - This has both a Home and Account controller and associated views. The Account Controller supports registration and login via either local accounts and via OAuth / OpenID providers.
  • Intranet - Like the Internet template, but it's preconfigured for Windows Authentication.
  • Mobile - This is preconfigured using jQuery Mobile and is intended for mobile-only sites.
  • Web API - This is preconfigured for a service backend built on ASP.NET Web API.

2012-08-29_17h26_24

Out of these templates, only one (the Internet template) uses SimpleMembership.

ASP.NET MVC 4 Basic template

The Basic template has configuration in place to use ASP.NET Membership with the Universal Providers. You can see that configuration in the ASP.NET MVC 4 Basic template's web.config:

<profile defaultProvider="DefaultProfileProvider">
  <providers>
    <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
  </providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
  <providers>
    <add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
  </providers>
</membership>
<roleManager defaultProvider="DefaultRoleProvider">
  <providers>
    <add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
  </providers>
</roleManager>
<sessionState mode="InProc" customProvider="DefaultSessionProvider">
  <providers>
    <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
  </providers>
</sessionState>

This means that it's business as usual for the Basic template as far as ASP.NET Membership works.

ASP.NET MVC 4 Internet template

The Internet template has a few things set up to bootstrap SimpleMembership:

2012-08-29_17h45_32

  • \Models\AccountModels.cs defines a basic user account and includes data annotations to define keys and such
  • \Filters\InitializeSimpleMembershipAttribute.cs creates the membership database using the above model, then calls WebSecurity.InitializeDatabaseConnection which verifies that the underlying tables are in place and marks initialization as complete (for the application's lifetime)
  • \Controllers\AccountController.cs makes heavy use of OAuthWebSecurity (for OAuth account registration / login / management) and WebSecurity. WebSecurity provides account management services for ASP.NET MVC (and Web Pages)

WebSecurity can work with any ExtendedMembershipProvider. There's one in the box (SimpleMembershipProvider) but you can write your own. Since a standard MembershipProvider is not an ExtendedMembershipProvider, WebSecurity will throw exceptions if the default membership provider is a MembershipProvider rather than an ExtendedMembershipProvider.

Practical example:

  1. Create a new ASP.NET MVC 4 application using the Internet application template
  2. Install the Microsoft ASP.NET Universal Providers for LocalDB NuGet package
  3. Run the application, click on Register, add a username and password, and click submit

You'll get the following execption in AccountController.cs::Register: To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider".

2012-08-29_18h34_13

This occurs because the ASP.NET Universal Providers packages include a web.config transform that will update your web.config to add the Universal Provider configuration I showed in the Basic template example above. When WebSecurity tries to use the configured ASP.NET Membership Provider, it checks if it can be cast to an ExtendedMembershipProvider before doing anything else.

So, what do you do?

Options:

If you want to use the new AccountController, you'll either need to use the SimpleMembershipProvider or another valid ExtendedMembershipProvider. This is pretty straightforward.

If you want to use an existing ASP.NET Membership Provider in ASP.NET MVC 4, you can't use the new AccountController. You can do a few things:

  1. Replace  the AccountController.cs and AccountModels.cs in an ASP.NET MVC 4 Internet project with one from an ASP.NET MVC 3 application (you of course won't have OAuth support). Then, if you want, you can go through and remove other things that were built around SimpleMembership - the OAuth partial view, the NuGet packages (e.g. the DotNetOpenAuthAuth package, etc.)
  2. Use an ASP.NET MVC 4 Internet application template and add in a Universal Providers NuGet package. Then copy in the AccountController and AccountModel classes.
  3. Create an ASP.NET MVC 3 project and upgrade it to ASP.NET MVC 4 using the steps shown in the ASP.NET MVC 4 release notes.

None of these are particularly elegant or simple. Maybe we (or just me?) can do something to make this simpler - perhaps a NuGet package. However, this should be an edge case - hopefully the cases where you'd need to create a new ASP.NET but use legacy ASP.NET Membership Providers should be pretty rare. Please let me (or, preferably the team) know if that's an incorrect assumption.

Membership in the ASP.NET 4.5 project template

ASP.NET 4.5 Web Forms took a different approach which builds off ASP.NET Membership. Instead of using the WebMatrix security assemblies, Web Forms uses Microsoft.AspNet.Membership.OpenAuth assembly. I'm no expert on this, but from a bit of time in ILSpy and Visual Studio's (very pretty) dependency graphs, this uses a Membership Adapter to save OAuth data into an EF managed database while still running on top of ASP.NET Membership.

2012-08-29_19h13_13

Note: There may be a way to use this in ASP.NET MVC 4, although it would probably take some plumbing work to hook it up.

How does this fit in with Universal Providers (System.Web.Providers)?

Just to summarize:

  • Universal Providers are intended for cases where you have an existing ASP.NET Membership Provider and you want to use it with another SQL Server database backend (other than SQL Server). It doesn't require agents to handle expired session cleanup and other background tasks, it piggybacks these tasks on other calls.
  • Universal Providers are not really, strictly speaking, universal - at least to my way of thinking. They only work with databases in the SQL Server family.
  • Universal Providers do not work with Simple Membership.
  • The Universal Providers packages include some web config transforms which you would normally want when you're using them.

What about the Web Site Administration Tool?

Visual Studio includes tooling to launch the Web Site Administration Tool (WSAT) to configure users and roles in your application.

image102[1]

WSAT is built to work with ASP.NET Membership, and is not compatible with Simple Membership. There are two main options there:

  1. Use the WebSecurity and OAuthWebSecurity API to manage the users and roles
  2. Create a web admin using the above APIs
  3. Since SimpleMembership runs on top of your database, you can update your users as you would any other data - via EF or even in direct database edits (in development, of course)

2012-08-29_19h34_46

Adding an Admin user to an ASP.NET MVC 4 application using a single drop-in file

I'm working on an ASP.NET MVC 4 tutorial and wanted to set it up so just dropping a file in App_Start would create a user named "Owner" and assign them to the "Administrator" role (more explanation at the end if you're interested).

There are reasons why this wouldn't fit into most application scenarios:

  • It's not efficient, as it checks for (and creates, if necessary) the user every time the app starts up
  • The username, password, and role name are hardcoded in the app (although they could be pulled from config)
  • Automatically creating an administrative account in code (without user interaction) could lead to obvious security issues if the user isn't informed

However, with some modifications it might be more broadly useful - e.g. creating a test user with limited privileges, ensuring a required account isn't accidentally deleted, or - as in my case - setting up an account for demonstration or tutorial purposes.

Challenge #1: Running on startup without requiring the user to install or configure anything

I wanted to see if this could be done just by having the user drop a file into the App_Start folder and go. No copying code into Global.asax.cs, no installing addition NuGet packages, etc. That may not be the best approach - perhaps a NuGet package with a dependency on WebActivator would be better - but I wanted to see if this was possible and see if it offered the best experience.

Fortunately ASP.NET 4 and later provide a PreApplicationStartMethod attribute which allows you to register a method which will run when the application starts up. You drop this attribute in your application and give it two parameters: a method name and the type that contains it. I created a static class named PreApplicationTasks with a static method named, then dropped this attribute in it:

[assembly: PreApplicationStartMethod(typeof(PreApplicationTasks), "Initializer")] 

That's it. One small gotcha: the namespace can be a problem with assembly attributes. I decided my class didn't need a namespace.

Challenge #2: Only one PreApplicationStartMethod per assembly

In .NET 4, the PreApplicationStartMethod is marked as AllMultiple=false, so you can only have one PreApplicationStartMethod per assembly. This was fixed in .NET 4.5, as noted by Jon Skeet, so you can have as many PreApplicationStartMethods as you want (allowing you to keep your users waiting for the application to start indefinitely!).

The WebActivator NuGet package solves the multiple instance problem if you're in .NET 4 - it registers as a PreApplicationStartMethod, then calls any methods you've indicated using [assembly: WebActivator.PreApplicationStartMethod(type, method)]. David Ebbo blogged about that here:  Light up your NuGets with startup code and WebActivator.

In my scenario (bootstrapping a beginner level tutorial) I decided not to worry about this and stick with PreApplicationStartMethod.

Challenge #3: PreApplicationStartMethod kicks in before configuration has been read

This is by design, as Phil explains. It allows you to make changes that need to happen very early in the pipeline, well before Application_Start. That's fine in some cases, but it caused me problems when trying to add users, since the Membership Provider configuration hadn't yet been read - I got an exception stating that "Default Membership Provider could not be found."

PreApplicationStartMethod fires before config is loaded

The solution here is to run code that requires configuration in a PostApplicationStart method. But how to do that?

Challenge #4: Getting PostApplicationStartMethod without requiring WebActivator

The WebActivator NuGet package, among other things, provides a PostApplicationStartMethod attribute. That's generally how I'd recommend running code that needs to happen after Application_Start:

[assembly: WebActivator.PostApplicationStartMethod(typeof(TestLibrary.MyStartupCode), "CallMeAfterAppStart")]

This works well, but I wanted to see if this would be possible without WebActivator. Hmm.

Well, wait a minute - WebActivator works in .NET 4, so clearly it's registering and calling PostApplicationStartup tasks somehow. Off to the source code! Sure enough, there's even a handy comment in ActivationManager.cs which shows where PostApplicationStartup tasks are being registered:

public static void Run()
{
    if (!_hasInited)
    {
        RunPreStartMethods();

        // Register our module to handle any Post Start methods. But outside of ASP.NET, just run them now
        if (HostingEnvironment.IsHosted)
        {
            Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(StartMethodCallingModule));
        }
        else
        {
            RunPostStartMethods();
        }

        _hasInited = true;
    }
}

Excellent. Hey, that DynamicModuleUtility seems familiar... Sure enough, K. Scott Allen mentioned it on his blog last year. This is really slick - a PreApplicationStartMethod can register a new HttpModule in code. Modules are run right after application startup, so that's a perfect time to do any startup stuff that requires configuration to be read. As K. Scott says, it's this easy:

using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;

[assembly:PreApplicationStartMethod(typeof(MyAppStart), "Start")]

public class CoolModule : IHttpModule
{
    // implementation not important 
    // imagine something cool here
}

public static class MyAppStart
{
    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(CoolModule));
    }
}

Challenge #5: Cooperating with SimpleMembership

The ASP.NET MVC Internet template includes SimpleMembership. SimpleMembership is a big improvement over traditional ASP.NET Membership. For one thing, rather than forcing a database schema, it can work with your database schema. In the MVC 4 Internet template case, it uses Entity Framework Code First to define the user model. SimpleMembership bootstrap includes a call to InitializeDatabaseConnection, and I want to play nice with that.

There's a new [InitializeSimpleMembership] attribute on the AccountController, which calls \Filters\InitializeSimpleMembershipAttribute.cs::OnActionExecuting(). That comment in that method that says "Ensure ASP.NET Simple Membership is initialized only once per app start" which sounds like good advice. I figured the best thing would be to call that directly:

new Mvc4SampleApplication.Filters.InitializeSimpleMembershipAttribute().OnActionExecuting(null);

I'm not 100% happy with this - in fact, it's my least favorite part of this solution. There are two problems - first, directly calling a method on a filter, while legal, seems odd. Worse, though, the Filter lives in the application's namespace, which means that this code no longer works well as a generic drop-in.

The simplest workaround would be to duplicate the relevant SimpleMembership initialization code into my startup code, but I'd rather not. I'm interested in your suggestions here.

Challenge #6: Module Init methods are called more than once

When debugging, I noticed (and remembered) that the Init method may be called more than once per page request - it's run once per instance in the app pool, and an individual page request can cause multiple resource requests to the server. While SimpleMembership does have internal checks to prevent duplicate user or role entries, I'd rather not cause or handle those exceptions. So here's the standard single-use lock in the Module's init method:

void IHttpModule.Init(HttpApplication context)
{
    lock (lockObject)
    {
        if (!initialized)
        {
            //Do stuff
        }
        initialized = true;
    }
}

Putting it all together

With all of that out of the way, here's the code I came up with:

The Verdict: Is this a good thing?

Maybe.

I think you'll agree that the journey was undoubtedly worthwhile, as it took us through some of the finer points of hooking into application startup, integrating with membership, and understanding why the WebActivator NuGet package is so useful

Will I use this in the tutorial? I'm leaning towards no - I think a NuGet package with a dependency on WebActivator might work better:

  • It's a little more clear what's going on
  • Installing a NuGet package might be a little less error prone than copying a file
  • A novice user could uninstall the package when complete
  • It's a good introduction to NuGet, which is a good thing for beginners to see
  • This code either requires either duplicating a little code from that filter or modifying the file to use the namespace

Honestly I'm undecided at this point, but I'm glad that I can weigh the options.

If you're interested: Why are you doing this?

I'm updating the MVC Music Store tutorial to ASP.NET MVC 4, taking advantage of a lot of new ASP.NET MVC 4 features and trying to simplify areas that are giving people trouble. One change that addresses both needs us using the new OAuth support for membership as much as possible - it's a great new feature from an application perspective, and we get a fair amount of beginners struggling with setting up membership on a variety of database and development setups, which is a distraction from the focus of the tutorial - learning ASP.NET MVC.

Side note: Thanks to some great help from Rick Anderson, we had a draft of the tutorial that was looking pretty good earlier this summer, but there were enough changes in ASP.NET MVC 4 all the way up to RTM that there's still some work to be done. It's high priority and should be out very soon.

The one issue I ran into with OAuth is that we still need an Administrative user who can edit the store's inventory. I thought about a number of solutions for that - making the first user to register the admin, or the first user to use the username "Administrator" is assigned to the Administrator role - but they both ended up requiring extra code; also, I worried that people would use that code without understanding it or thinking about whether it was a good fit.

Posted by Jon Galloway | 1 comment(s)
Filed under: ,

Guest on Cloud Cover Show: What's new in Visual Studio 2012, ASP.NET 4.5, ASP.NET MVC 4 and Windows Azure Web Sites

I was the guest star on the Cloud Cover Show this past week talking about what's new for web developers in Visual Studio 2012, ASP.NET 4.5, ASP.NET MVC 4, and Windows Azure Web Sites. As a remote presenter (I recorded my screencast while at ThatConference in Wisconsin Dells), I was given the floating head treatment when first introduced:

I gave a 20 minute walkthrough, showing the following highlights:

  • File / New Project with ASP.NET 4.5
    • NuGet packages in the default template (and why that's good)
    • Adaptive rendering in the default ASP.NET 4.5 project template
    • A few Visual Studio 2012 web developer features (CSS color picker, fonts, HTML5 snippets)
    • Strongly typed data control
    • Model binding support
    • Note: ASP.NET 4.5 requires .NET 4.5, will be supported in Azure some time after .NET 4.5 GA
  • File / New Project with ASP.NET MVC 4
    • Supports both .NET 4 and .NET 4.5
    • New ASP.NET MVC 4 project types
    • Same template experience as ASP.NET 4.5 Web Forms - adaptive rendering, etc.
    • Code-based configuration moved to App_Start
    • OAuth login support
    • Add new ASP.NET Web API controller
    • Show Web API
  • Deploy MVC 4 Application to Windows Azure Web Sites
    • Create new website in Windows Azure Web Sites portal, download publish profile
    • Visual Studio 2012 Web Publish dialog
    • Publish and demo site

Thanks to Nick and Cory for inviting me on the show. I hope my disembodied head will be invited back on Cloud Cover again soon.

Posted by Jon Galloway | with no comments

ASP.NET 4.5, ASP.NET MVC 4, ASP.NET Web Pages 2, and Visual Studio 2012 for web developers: Part 1

Wow, yesterday was a big day for ASP.NET developers! In addition to some other very cool things, Microsoft announced Visual Studio 2012, ASP.NET 4.5, ASP MVC 4 were released to manufacturing. Hooray! So what now?

Get The Bits

The easiest way to get all the new goodies is to install Visual Studio 2012. You've got a few options there:

We've got the top download links listed for you on the ASP.NET site as well.

Note: Just install what you'll use

As before, my personal recommendation is to just install the Visual Studio components you're expecting to use. If you never do C++ development, installing all the C++ support just means that your install will take longer, take up more space, make service packs take longer to install, increase the Windows Updates you'll encounter, etc. You can always modify the installed features if you need to later. Here's what I'm installing (I'm including Blend because I've been doing a bit of Windows 8 app development - it works with both both XAML and HTML Windows Store apps).

Here's what I've got selected:

Visual Studio 2012 Install

Learning Resources On The New Stuff

There is a ton of new stuff, and it's easy to get overwhelmed. Here's a quick overview of some of the top resources before we dig into specifics:

ASP.NET 4.5

Model Binding and Strongly Typed Data Controls

The Model Binding and Strongly Typed Data Controls in ASP.NET Web Forms are really cool. Damian Edwards (who helped found the Web Forms MVP project before joining the ASP.NET team) and team pulled of an amazing feat: they brought the model binding and strong typing features of ASP.NET MVC into ASP.NET Web Forms in a way that still fits with Web Forms. Essentially, it's something like an object data source approach, but so much smoother.

First, the strongly typed data controls allow you to define a model type for a control. That allows you to replace string-based data binding expressions with strongly typed binding expressions:

Hooray!

Also, notice that the FormView above isn't binding to a data source, it's got methods defined for Select / Insert / Update. The select method looks like this:

public Issue GetIssue([Control("issuesGrid")]int? id)
{
    return _db.Issues.Find(id);
}

The Insert and Update methods take advantage of the binding features in MVC, allowing you to bind to control values, URL and other contextual values, and custom value providers:

public void InsertIssue()
{
    var issue = new Issue();

    TryUpdateModel(issue);

    if (ModelState.IsValid)
    {
        _db.Issues.Add(issue);
        SaveChanges(issue);
    }
}

This eliminates all the tedious and error-prone "this parameter equals that control value, this other parameter equal that other control value, kill me now" type code. Note that TryUpdateModel call (familiar to MVC devs) which will automatically apply any validation rules defined via data annotations on the model class.

I gave a presentation this week at ThatConference looking at the feasibility of mixing ASP.NET Web Forms and MVC in the same project, and my conclusion was that the best way to get started is to get your Web Forms apps on ASP.NET 4.5 so you can start sharing models and converting traditional Web Forms code towards this approach, which is a lot more similar to MVC.

Bundling and Optimization

Bundling and Optimization in ASP.NET 4.5 and ASP.NET MVC 4 is based on an optimization system the MSN team developed called WebGrease. The core has been battle tested (MSN is a top 20 site) and performs really well. The bundling and optimization system optimizes user experience by compressing and combining CSS and JavaScript so your users get a faster experience and you minimize your bandwidth.

You'll see default bundles defined in \App_Start\BundleConfig.cs in an ASP.NET 4.5 Web Forms or ASP.NET MVC 4 application. Here's what it looks like in Web Forms:

public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
          "~/Scripts/WebForms/WebForms.js",
          "~/Scripts/WebForms/WebUIValidation.js",
          "~/Scripts/WebForms/MenuStandards.js",
          "~/Scripts/WebForms/Focus.js",
          "~/Scripts/WebForms/GridView.js",
          "~/Scripts/WebForms/DetailsView.js",
          "~/Scripts/WebForms/TreeView.js",
          "~/Scripts/WebForms/WebParts.js"));

    bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
        "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
        "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
        "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
        "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));

    // Use the Development version of Modernizr to develop with and learn from. Then, when you’re
    // ready for production, use the build tool at http://modernizr.com to pick only the tests you need
    bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
        "~/Scripts/modernizr-*"));
}

Now you can just use any of those bundles in your application:

 <asp:PlaceHolder runat="server">        
         <%: Scripts.Render("~/bundles/modernizr") %>
         <%: Scripts.Render("~/bundles/jquery") %>
         <%: Scripts.Render("~/bundles/jqueryui") %>
</asp:PlaceHolder>

One of my favorite parts about this is that we can make bundles that accomodate version numbers in script names, so updating jQuery (maybe via NuGet if you're awesome) doesn't require *any* code / markup changes:

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/jquery-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
            "~/Scripts/jquery-ui-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
            "~/Scripts/jquery.unobtrusive*",
            "~/Scripts/jquery.validate*"));

For more on using Bundling and Minification in Web Forms, see Adding Bundling and Minification to Web Forms (Rick Anderson) or either of the following videos:

Quick intro by Scott Hanselman:

Deep Dive by Howard Dierking:

Updated Project Templates (Adaptive Rendering)

Both the new ASP.NET Web Forms and MVC project templates have been redesigned. They don't just look nicer, they leverage web standards to automatically adapt to screen resolution (HTML and CSS only, no JavaScript or server-side code required).

Here's how the default template looks in a wide screen (> 850px):

2012-08-16_16h50_39

Resizing the screen automatically applies new styles via CSS media queries so it's easier to read and navigate:

2012-08-16_16h50_47

I wrote about how this works in pretty gory detail in the context of ASP.NET MVC 4, but it's working exactly the same in ASP.NET Web Forms 4.5.

OAuth and OpenID support

The default project templates now have built-in support for user registration and log in via OAuth and OpenID. They're making use of the excellent DotNetOpenAuth library, included as  a NuGet package so you can update later if you'd like.

Your OAuth and OpenID providers are registerd in \AppStart\AuthConfig.cs. Initially there are some examples that are all commented out (this is something you should do explicitly, and OAuth providers require you fill in an app id and app secret):

public static void RegisterOpenAuth()
{
    // See http://go.microsoft.com/fwlink/?LinkId=252803 for details on setting up this ASP.NET
    // application to support logging in via external services.

    //OpenAuth.AuthenticationClients.AddTwitter(
    //    consumerKey: "your Twitter consumer key",
    //    consumerSecret: "your Twitter consumer secret");

    //OpenAuth.AuthenticationClients.AddFacebook(
    //    appId: "your Facebook app id",
    //    appSecret: "your Facebook app secret");

    //OpenAuth.AuthenticationClients.AddMicrosoft(
    //    clientId: "your Microsoft account client id",
    //    clientSecret: "your Microsoft account client secret");

    //OpenAuth.AuthenticationClients.AddGoogle();
}

Uncommenting and filling in the key / secret information for OAuth clients changes your Log In screen to allow users to access your site as members without creating an account:

2012-08-16_16h59_15

For more information on configuring specific clients, see Pranav's blog post: OAuth/OpenID Support for WebForms, MVC and WebPages.

And much more...

There's of course a lot more, these are just some of my favorite features. Let's move on to ASP.NET MVC 4!

ASP.NET MVC

Mobile Features

ASP.NET MVC 4 has several great options to help you build sites that work well on mobile devices. Here's a diagram that helps illustrate it:

2012-08-16_19h10_27

I talked about all three of these and overviewed ASP.NET MVC 4 mobile features at aspConf:

We've already talked about adaptive rendering in Web Forms, it works the same here.

Display Modes allow you to create views which are served to mobile devices, and you can create your own display modes to target specific devices. Since the display modes are defined in code, they can be based on any logic you want - database queries, user cookies, day of the week, whatever. Here's how a display mode is defined:

DisplayModeProvider.Instance.Modes.Insert(0, new 
DefaultDisplayMode("iPhone") 
{ 
    ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf 
        ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0) 
 });

Finally, the mobile template (based on jQuery Mobile) makes it easy to create web applications that are optimized for mobile users.

To learn more about what you can do using the Mobile Project Template, see Rick Anderson's tutorial: ASP.NET MVC 4 Mobile Features.

K. Scott Allen did a great presentation on the Mobile Project Template and jQuery Mobile at NDC 2012:

Scott Allen - ASP.NET MVC and jQuery Mobile from NDCOslo on Vimeo.

Async Support

ASP.NET MVC has had async support since MVC 2, but it was hard. It gets a lot easier in MVC 4 due to async and await. Once again, I'll refer to Rick Anderson's excellent tutorial: Using Asynchronous Methods in ASP.NET MVC 4. Here's an example of an async controller method that calls three long running services and returns to the user when all three are complete:

public async Task<ActionResult> PWGasync() 
{ 
    ViewBag.SyncType = "Asynchronous"; 
    var widgetService = new WidgetService(); 
    var prodService = new ProductService(); 
    var gizmoService = new GizmoService(); 
 
    var widgetTask = widgetService.GetWidgetsAsync(); 
    var prodTask = prodService.GetProductsAsync(); 
    var gizmoTask = gizmoService.GetGizmosAsync(); 
 
    await Task.WhenAll(widgetTask, prodTask, gizmoTask); 
 
    var pwgVM = new ProdGizWidgetVM( 
       widgetTask.Result, 
       prodTask.Result, 
       gizmoTask.Result 
       ); 
 
    return View("PWG", pwgVM); 
}

OAuth and OpenID support

Yes, already mentioned before in Web Forms, but it's also in MVC 4, and it's really cool.

The ASP.NET MVC 4 Release Notes cover these and other features in more detail.

ASP.NET Web API

ASP.NET Web API is officially released! I did a series of screencasts earlier this year that introduce ASP.NET Web API. They're slightly out of date on a few technical details (I'll be updating them soon) but explain what it is and why you'd use it; here's the first in the series:

[Video and code on the ASP.NET site]

The team wrote some great posts on recent features:

Wow, that's a lot, and we didn't even get into Visual Studio 2012 features for Web Developers! Looks like it's time for a break, with a part 2 to follow...

Zenbook UX31 as a Windows 8 laptop

I've written two previous reviews of a Zenbook UX31 Ultrabook. Short summary: I really like the form factor, build quality performance, and battery life. Here's a final review with a look at Zenbook as a longterm Windows 8 laptop.

Installing Windows 8

I installed the latest public release of (Windows 8 Release Preview). As with every Windows 8 install I've done, this was a very fast install. I opted for a pave / reinstall because everything I cared about was already being sync'd via Skydrive or was in source control. Yay clouds! After a few minutes, it was installed and I was ready to log in. I've got Windows 8 installed on some other computers, so it already had my desktop and other settings set up.

The Zenbook came preinstalled with Windows 7 and a bunch of utilities including an ASUS Update manager thing. My initial reaction was that it was a bit much. They'd obviously put some thought and expense into the unbox / first run experience, and the utility overload thing was really the only thing I encountered that marred that experience. So when I started with a fresh install on Windows 8, I was interested to see what a world without ASUS utilities would look like. Turns out, that world looks pretty fine. The devices and drivers all worked fine. Really, they could have given me a better experience with a desktop shortcut (or on Windows 8, maybe a preinstalled Metro app or something).

Just to see if I was missing anything, I tried to install the ASUS Update whatever thing. It said the operating system wasn't supported. I could probably have installed in compatibility mode, but I couldn't think of a good reason. In the end I just installed the Elantech touchpad drivers for good luck and called it quits. Because, you know, everything was working just fine. So, to sum up so far: easy Windows 8 install, all the drivers worked. Well, mostly. More on that later.

Windows 8 Snap on a 1600x900 screen

One thing I've always liked about the Zenbook is the screen resolution: 1600x900. The minimum screen resolution for snapping Windows 8 modern UI apps is 1366x768, which meant it's never worked on my other small laptop. It's okay at 1366, but it's really nice at 1600 width. Since the snap view is always 320px, a 1366 resolution gets you a 1024 main area; a 1600 width display get you a much more workable 1280px for the main content area.

As of now, my favorite Windows 8 apps for snapping are media players. Here are a few examples. First, the SlapDash Podcast app snapped while working in Visual Studio:
Snapping

And the Slacker Radio, again with Visual Studio:

Snapping 2

So far I'll admit this is just kind of neat, but I'm really looking forward to use this with some interesting "snappable" apps as they arrive in the store.

About Those Drivers...

Not content to leave well enough alone, I wanted to compare the Windows 8 Windows Experience Index with what I was getting on Windows 7. So here's the Windows 8 WEI:

WEI

And here are the Windows 7 numbers:

The memory performance stayed the same, the processor went up a little, the hard disk numbers went up a lot (maybe some drive optimizations in Windows 8?) and the graphics numbers went down. So suddenly (and illogically) I felt I had to get those graphic perf numbers up.

It was not to be.

I went over to the ASUS driver download site and tried a bunch of different Windows 7 drivers (Windows 8 aren't available yet). The video drivers wouldn't install, even in compatibility mode. Then I tried the Windows 8 video drivers on Intel's site, but those were blocked, too.

I read a bunch of Windows 8 super-mega-beta-info sites, and a few people got newer graphics drivers to install, but it sounded like a lot of work which was very likely not to pay off. I'm sure they'll have updated drivers out soon after Windows 8 releases, but for now I'd consider the optimized drivers unavailable. Doesn't really matter, the generic Windows 8 drivers work just fine.

One Tiny, Huge Problem

As I said, I've really liked this laptop. The form factor's nice, it's snappy, the battery really lasts. It's convinced me that there's a useful space between small devices and a big 'ol laptop. It feels like the best of both - the convenience of a device, but the power to handle real, significant work. It feels so much like a device that I keep touching the screen, expecting it to be multitouch. And it looks like future versions will be - back in June I saw this in the headlines: ASUS shows off a touchscreen Zenbook Prime Ultrabook.

But then I hit a problem.

The charger plug is tiny little barrel connector. I'm used to barrel connectors, but this one's so thing that it's more fragile than I'm used to. I'm pretty careful, but it's gotten bent up a bit. You might be able to see it in the picture below, although it's pretty slight.

IMAG0233

But here's the problem: it the connection didn't always work. I'd plug it in and it wouldn't charge unless I jiggled it around a little. After a while, it got tricky enough that I stopped bringing it on trips, because I wasn't sure if it was going to work.

So, fine, time to get a replacement charger. Except I think it's worse than that. The connection's weird enough that I read up a bit more, and it sounds like sometimes the charging connection on the motherboard breaks [1] [2]. Not it looks like I'm probably going to need to RMA it. I've heard good things about ASUS support and feel pretty confident they'll fix it, but I'll definitely be more careful with the charging connection if they do fix it.

Summary

Excepting the charger, I think this is a great device. I'm really happy to see what $1000 buys in a laptop these days, and was happy to see how well it handled Windows 8.

<disclaimer> I received an ASUS Zenbook 31 for free in the hope that I would mention it on my blog. Regardless, I only recommend things I personally endorse and would recommend. I'm disclosing this in accordance with the FTC's Guides Concerning the Use of Endorsements and Testimonials in Advertising. Just in case, I also cleared it with my employer's legal / corporate affairs team. I also made sure the agreement said that my review would be my honest opinion, and that's what this is. This is my opinion alone, and doesn't necessarily reflect the views of my employer.</disclaimer>

Posted by Jon Galloway | with no comments
Filed under:
More Posts