Setting up Mono 2.8 with Asp.Net 4.0 and MVC2 on Ubuntu with MySql Membership

I’ve had a few requests to write a full walk-through installing Mono 2.8 w/ Asp.Net 4.0 since I posted an install script for Mono 2.8 on Ubuntu and Fedora.  So here it is! We’re going to be setting up a new server from scratch then installing just the basics of Mono 2.8 (Mono, GTK, GDI, XSP, & Mod_Mono). Then we’ll use that to setup a new Apache server with a basic Asp.Net 4.0 MVC2 application w/ MySql Membership.  That sounds like a lot, but it’s really very straight forward. So lets get started!

Update! 1/30/2011

Mono 2.8.2 has become available since this article was posted and is highly recommended due to security enhancments. Refer to install script for Mono 2.8.2 instead of 2.8. Pay close attention to your virtual host config so you use the correct mono install prefixes. Check MonoServerPath and MonoSetEnv directives below.

Setup Your Server

For our server, we’re going to setup a VM of 32 bit Ubuntu Server 10.04 LTS using VirtualBox. You can use whichever VM software you want or setup a physical server. This is just an easy sandbox system for this walkthrough. I noticed VirtualBox requires hardware virtualization for 64bit guest systems; keep that in mind if you test with a 64bit server.

I applied default configuration settings to my VM with 512 RAM and a bridged network interface so I could communicate with it directly from another machine. After that, I mounted the Ubuntu Server install ISO to the disc drive and booted it into installation.  During installation, the only package option I enabled was the OpenSSH Server (use arrows, spacebar, and tabs to move around the installer).  I’m not going to worry about other things like a web server since the Mono installer script will do that for us. Once you’ve completed the installer, unmount the ISO, boot up, and login. It defaults to using DHCP for ethernet, so find the current IP using:

ifconfig eth0

Using the IP Address listed here, connect to the box with an SSH client of your choice (I tend to use Putty or the OpenSSH client built into msysGit on Windows).  Immediately update your server. If you haven’t used “sudo” before, it will request your sudo password, which is just your normal account password you setup during install.

ssh nathan@ubuntu  
sudo apt-get update  
sudo apt-get dist-upgrade  
sudo apt-get upgrade -y

 

Install Mono

Once you’re updated, we can download the install script and install Mono, Apache, and dependencies.

wget --no-check-certificate https://github.com/nathanb/iws-snippets/raw/master/mono-install-scripts/ubuntu/install_mono-2.8.shchmod 755 install_mono-2.8.sh./install_mono-2.8.sh

This will re-run another update and then download, compile, and install everything required to run Mono 2.8. It will also compile and install other Mono-related items like: GTK, GDIPlus, XSP, and Mod_mono. Once it completes, update your system path and reboot. Edit “/etc/environment” and insert “/opt/mono-2.8/bin:” to the beginning of the PATH value.

Once completed, refer to this more recent post about setting up MVC, which also includes information about setting up your environment for the new install. You can test it with “mono –V”.

nathan@ubuntu:~$ mono -V  
Mono JIT compiler version 2.8 (tarball Mon Nov 15 14:39:28 CST 2010)  
Copyright (C) 2002-2010 Novell, Inc and Contributors. www.mono-project.com  
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  x86
        Disabled:      none
        Misc:          debugger softdebug
        LLVM:          supported, not enabled.
        GC:            Included Boehm (with typed GC and Parallel Mark)
nathan@ubuntu:~$

 

So now we just need to configure Apache. The install process copied a mod_mono.conf to the directory: /etc/apache2.  You’ll need to enable this by moving it to /etc/apache2/mods-available and creating a link to that file at /etc/apache2/mods-enabled.

cd /etc/apache2  
sudo mv mod_mono.conf mods-avail*  
sudo ln -s /etc/apache2/mods-available/mod_mono.conf /etc/apache2/mods-enabled/mono.conf

#optionally restart apache server. more changes coming
sudo service apache2 restart

 

Configure Website

We’re using ModMono to handle Asp.Net requests with Apache. I’ve mentioned before that I’m a big fan of the Mono website’s ModMono configuration tool. We can use that tool to build our initial VirtualHost configuration. Then change the MonoServerPath to be the one we installed at: “/opt/mono-2.8/bin/mod-mono-server4”  When you’re done, upload (or move) this folder to your home directory on the server.

NOTE: We’re using mod-mono-server4 for Asp.Net 4.0 runtime. You can use mod-mono-server2 for 2.0-3.5 Asp.Net and mod-mono-server for 1.0-1.1 Asp.Net.

NOTE: If you’re unfamiliar with copying files from your Windows box to a Linux one, you can use FileZilla with an SSH (SFTP) connection. Connect to something like: “sftp://username@host” then treat it like any other FTP connection.

Here we’ll setup our web folders, permissions, move our configuration file, link it, and restart the web server. Our generated configuration file is named ubuntu.conf and was uploaded to my home folder.

sudo mkdir /srv/www  
sudo mkdir /srv/www/ubuntu  
sudo chown root:www-data /srv/www/ubuntu -R  
sudo chmod 775 /srv/www/ubuntu -R  
mv ~/ubuntu.conf /etc/apache2/sites-available  
cd /etc/apache2/sites-en*  
sudo ln -s ../sites-available/ubuntu.conf "000-ubuntu.conf"  
sudo service apache2 restart

NOTE: I prefixed the link name with “000” since Apache loads configuration files in alphabetical order. It’s not necessary for this scenario, but if you’re running multiple virtual hosts and want a “default”, this is good to know. Make your default VirtualHost load first.

You might also want to give yourself permissions to write to the new web directories for publishing sites. The easy way to do this (maybe not best practice), would be to add yourself to the www-data group used by apache & mono, which we just setup with full access.

sudo usermod -a -G www-data your_username

Later when we upload the site, this will be a factor. The alternative will be to upload your published site to your home directory and then with an ssh console, sudo mv the published files to the web directories if you choose not to add yourself to the www-data group.

For this sample, we used all the default parameters in our configuration. Take note that debugging is enabled. I also left IO Mapping enabled to allow Mono to treat file system paths as case insensitive. You can improve performance by disabling IO Mapping. The VirtualHost configuration looks like this:

<VirtualHost *:80>  
  ServerName ubuntu
  ServerAdmin web-admin@ubuntu
  DocumentRoot /srv/www/ubuntu
  # MonoServerPath can be changed to specify which version of ASP.NET is hosted
  # mod-mono-server1 = ASP.NET 1.1 / mod-mono-server2 = ASP.NET 2.0
  # For SUSE Linux Enterprise Mono Extension, uncomment the line below:
  # MonoServerPath ubuntu "/opt/novell/mono/bin/mod-mono-server2"
  # For Mono on openSUSE, uncomment the line below instead:
  MonoServerPath ubuntu "/opt/mono-2.8/bin/mod-mono-server4"

  # To obtain line numbers in stack traces you need to do two things:
  # 1) Enable Debug code generation in your page by using the Debug="true"
  #    page directive, or by setting <compilation debug="true" /> in the
  #    application's Web.config
  # 2) Uncomment the MonoDebug true directive below to enable mod_mono debugging
  MonoDebug ubuntu true

  # The MONO_IOMAP environment variable can be configured to provide platform abstraction
  # for file access in Linux.  Valid values for MONO_IOMAP are:
  #    case
  #    drive
  #    all
  # Uncomment the line below to alter file access behavior for the configured application
  MonoSetEnv ubuntu MONO_IOMAP=all;LD_LIBRARY_PATH=/opt/mono-2.8/lib:$LD_LIBRARY_PATH;PATH=/opt/mono-2.8/bin:$PATH
  #
  # Additional environtment variables can be set for this server instance using
  # the MonoSetEnv directive.  MonoSetEnv takes a string of 'name=value' pairs
  # separated by semicolons.  For instance, to enable platform abstraction *and*
  # use Mono's old regular expression interpreter (which is slower, but has a
  # shorter setup time), uncomment the line below instead:
  # MonoSetEnv ubuntu MONO_IOMAP=all;MONO_OLD_RX=1

  MonoApplications ubuntu "/:/srv/www/ubuntu"
  <Location "/">
    Allow from all
    Order allow,deny
    MonoSetServerAlias ubuntu
    SetHandler mono
    SetOutputFilter DEFLATE
    SetEnvIfNoCase Request_URI "\.(?:gif|jpe?g|png)$" no-gzip dont-vary
  </Location>
  <IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript
  </IfModule>
</VirtualHost>

 

Create and Publish Asp.Net MVC2 Application

In Visual Studio 2010, select the File menu –> New –> Project. In the new project dialog near the top center, select the .NET Framework 4.  Then on the left, select Web (under C#), and choose ASP.NET MVC2 Web Application.  There are a few key items we’ll need to remove to make this compatible with Mono.

First, open the ~/Models/AccountModels.cs file and remove all data annotations attributes from the three model classes’ properties. You may leave “DisplayName”. I’ve found (without investing a lot of time in this), that the data annotations doesn’t seem to work out of the box. Removing these attributes will allow it to run properly under Mono. Keep in mind, this also means, you’ll need to write your own server-side validation. It’s not too painful; we’ve been doing this for  years anyway.

Finally, build and test-run the site from visual studio. It should work fine with the built-in Casini test web server. If all is well, right click on the project (in solution explorer) and Publish the site. Publish it to the file system and choose a destination folder. You don’t need to include the App_Data folder, and you can delete all files before publish if you want.

Upload the published files to the server using FileZilla, and copy them to /srv/www/ubuntu.  Apache doesn’t need to be restarted when you update web files and assemblies. You should now be able to browse the site at http://yourtestserver_host/.  All should work except membership since we haven’t configured the database yet.

 

Setup Membership with MySql

Awhile back, I wrote a post dedicated to MySql Membership with a simple config generation tool do this for you. Checkout the post for more details after setting up your database server. Setting up membership is identical between Asp.Net MVC and web forms. Install and configure MySql Server first. Then setup a placeholder database for the membership data. The first time you use membership during runtime will automatically generate the database schema.

Install MySql and Setup Empty Membership Database

sudo apt-get install mysql-server

NOTE: The default install will lock it down to localhost access only, which is good for a server. But for test, if you want to access it from your machine, you’ll have to tell it to bind to a LAN address (instead of 127.0.0.1). Take a peek at /etc/msyql/my.cnf file to update this setting. The comments describe the change.

I’m giving application user full access (including schema change privileges) to the membership database. MySql Memebrship provider updates its own schema as you update the provider.  Checkout the MySql docs if you want to limit account privileges.

nathan@ubuntu:~$ mysql -h localhost -u root -p  
mysql: create database membership;  
mysql: grant all privileges on membership.* to 'aspnet_user'@'localhost' identified by 'secret_password';  
mysql: quit

Configure Membership

I used my configuration tool and generated a simple membership config.  Copy the membership sections to your into the “system.web” section of your root web.config and setup a connection string with the same name as the one used in membership. (“Default” is used here).

Next, if you’re using a factory default version of MySql Connector/NET, you’ll need to download its source and recompile it with .NET 4.0 profile. I found while doing this that the published 2.0 version seemed to blow up when referencing transaction methods. I found that it was a known bug and should be resolved in upcoming versions of the connector. For now, you’ll need to recompile MySql.Web and MySql.Data with a 4.0 profile if you want it to work under Mono with membership.  The sample version packaged here has that done already.

NOTE: The version of MySql .NET Connector I was using at the time I wrote this tool (6.3.5) didn’t support hashed passwords with Mono runtime. I made a minor tweak to this version that will disable the Mono runtime check for hashed passwords and will allow it (since it does work in the current version of Mono). 

<authentication mode="Forms">  
    <forms
      loginUrl="~/Account/Logon"
      timeout="30"
      name="6ad58f32-93d5-463f-9d99-835942aca8f5"
      path="/"
      requireSSL="false"
      slidingExpiration="true"
      defaultUrl="Default.aspx"
      enableCrossAppRedirects="false"/>
</authentication>

<membership defaultProvider="MySqlMembershipProvider">
    <providers>
        <clear/>
        <add name="MySqlMembershipProvider"
              type="MySql.Web.Security.MySQLMembershipProvider, mysql.web"
                connectionStringName="Default"
                enablePasswordRetrieval="false"
                enablePasswordReset="true"
                requiresQuestionAndAnswer="false"
                requiresUniqueEmail="true"
                passwordFormat="hashed"
                maxInvalidPasswordAttempts="5"
                minRequiredPasswordLength="6"
                minRequiredNonalphanumericCharacters="0"
                passwordAttemptWindow="10"
                applicationName="/"
                autogenerateschema="true"/>
    </providers>
</membership>

<roleManager enabled="true" defaultProvider="MySqlRoleProvider">
    <providers>
        <clear />
        <add connectionStringName="Default"
            applicationName="/"
            name="MySqlRoleProvider"
            type="MySql.Web.Security.MySQLRoleProvider, mysql.web"
            autogenerateschema="true"/>
    </providers>
</roleManager>

<profile>
    <providers>
        <clear/>
        <add type="MySql.Web.Security.MySqlProfileProvider, mysql.web"
              name="MySqlProfileProvider"
              applicationName="/"
              connectionStringName="Default"
              autogenerateschema="true"/>
    </providers>
</profile>

 

Also for Mono, I added the DbProviderFactories section for MySql data in case it wasn’t already setup. Insert this adjacent to the <system.web> section.

<system.data>  
    <DbProviderFactories>
        <clear/>
        <add name="MySQL Data Provider"
            description="ADO.Net driver for MySQL"
            invariant="MySql.Data.MySqlClient"
            type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data"/>
    </DbProviderFactories>
</system.data>

Once updated, you should now be able to run the MVC app and register/authenticate user accounts. If you have any problems, double-check your data source value, username and password. Make sure you can connect to it with a mysql client and your app credentials.  Also I find the Asp.Net Configuration Tool built-in to Visual Studio helpful when testing Membership configuration. It will show you a number of users and roles if membership is configured properly.

 

Sample

Grab the sample from here or github. Includes: Web App, dependencies, & virtual host config. Comments, enhancements, pull requests, are welcome!

 

Wrap-Up

So now we’re done. The next thing you should checkout is how to use inversion of control with MVC2. I’ve been writing applications lately using SubSonic3 (repository mode) and Unity Application Block 2.0 for dependency injection. Coupled with MvcContrib test helpers and Rhino Mocks, this is a powerful combination for writing rock solid, testable applications that run on Windows AND Mono.

Here are a few useful links for this post:

comments powered by Disqus