Main Points

Our initial MySQL installation and setup is complete and we've created our first .NET code to display MySQL data on a webpage from one of the default tables, the one which stores character sets. Now let's build a couple custom tables and see if we can display our own live data using MySQL. Creating the tables will mean that I have custom DB information which I can only deploy to the website by copying not only the website but the database up to the production server. This is a critical step that needs to work, but if it does, I'll have created a working demonstration of every goal I'd hoped to tackle with MySQL. That would be unbelievable, yet we'd be able to run Earth Chronicle from a genuine enterprise class database, so let's get to work!

Creating MySQL Tables

I've experimented with the MySQL migration tool. Unfortunately, it seems to be designed for enterprise class databases; it's asking for a lot of parameters that I don't recognize as relevant to the Testing.mdf file which I wanted to migrate. Oh well, I'll have to create a few small tables manually.

I opened up MySQL Administrator to see about creating a custom database. In the previous examples I displayed data from an existing MySQL table which stores character set data. It's part of the install package so it required no custom tables. Now, I need to back up and restore the databases to see if I can really copy information back and forth; naturally, that means I need a couple tables to copy. In the left column, I select Catalogs which opens a new column underneath to choose a schema (plural, schemata). MySQL is a database, but it can actually set up multiple sets of tables. These sets of tables are called schema in MySQL; anywhere else they'd be called a database, which means MySQL is a tool for managing many databases (or in its own terms, schemata). This is important because the name of the schema, e.g. EarthChronicle, must be inserted into your connection string using "database=EarthChronicle;".

Press Ctrl+N to create a new schema or right click a blank space in the schemata column and select it from the context menu. If you wish to work with a schema that's already created, just select it and its tables will appear in a list in the main panel. You can also select other tabs to work with stored procedures, indices, and views, but for now I just need a couple new tables. I click the Create Table button at the bottom of the panel which opens the MySQL Table Editor dialog. In the Table Name text box in the top left corner, I name my tables starting with the word "testing" so there's no confusion that these could be live data tables. Next, I chose Table Options on the top set of tabs, and reset the default selection, InnoDB table engine, to MyISAM which is supposed to be the MySQL default. Then I return to the Columns and Indices tab at top and choose Column Details from the lower set of tabs.

I really like the Table Editor. Things you had to click through tree views for in VWD, are all right at your fingertips here: setting primary keys, autoincrement, column names, data types, default values, etc. By the time I finish setting up the three tables I'm going to use for this test, it's already faster for me to build a database with MySQL Administrator than it ever was with VWD. I'm not pleased with the fact that the data types are not easily available from the table editor or from help. I had to hit the internet to see what my choices were and how they were different. Now that I've done that, however, I can't imagine going back.

I was also able to use the Query Browser to input some quick data to mimic the link factory. Here the Schemata column is on the right side of the main panel; I can't figure out why the UI is different from MySQL Administrator, but it's a simple clear UI. I expand the schema to reveal the tables I built in MySQL Administrator, and double click the table I want to open. This populates a basic display query in the SQL Query Area, and I click the Execute button on the toolbar (a green button with a lightning bolt). The database table data appears in the main area, in my case I have none yet. I click the edit button at the bottom of the main area (centered). I can now select a cell at the beginning of a blank row and simply enter data and tab to switch to the next cell. I repeat this process for each table until I have enough data to create three distinct links.

It took twice as long to explain as to set up. This is really a slick program; I take back some of what I said about it being copyrighted. Now that we've got custom data in it, let's see if we can get it out.

Modifying the .NET Code for Custom Data

First, I rewrote the code behind to take a new connection string, so it would throw an error. The application indeed throws an error, saying it can't find and instance of the connection object I've specified. Remember that MySQL manages multiple databases (or schemata), so I need a new connection string to connect to the custom data I just created. I didn't just create new tables, I created a new schema, so while the connection string I use for this page is nearly identical to what I used last page, I need to change "database=information_schema; to database=EarthChronicle;. I make these changes in the connection strings to the "permanent" web.config files for both the dev box and the production server. Next, I replace the Web.config file with the permanent dev box file, and when I test it, the page can now establish a connection

I've purposely neglected to configure the user access for this database to make sure that the proper error is thrown. :) And indeed, .NET returns an "Access denied for user..." error when it tries to open the connection. You can see that as I'm building step by step to make sure that the applications behavior is correct. That means that the application succeeds correctly and that it Fails correctly. Since the proper access errors are working, I'm now comfortable opening MySQL Administrator's User Administration to give the AspNet user account SUID permissions (SELECT, UPDATE, INSERT, and DELETE). Now the .NET code has permission to access the database, and I get an error message that the table does not exist in the database. It's time to work out my SQL statement.

SELECT t.`PageID`, testingwebsitepaths.`WebsitePathName`, testingfolderpaths.`FolderPathName`, t.`PageName` FROM testing t
INNER JOIN testingwebsitepaths ON t.WebsitePathID = testingwebsitepaths.WebsitePathID
INNER JOIN testingfolderpaths ON t.FolderPathID = testingfolderpaths.FolderPathID

If you take a look at the original SQL for MS SQL Server, this is pretty similar but there are differences...

SELECT PagePaths.PageID, PagePaths.PageName, WebsitePaths.WebsitePathName, FolderPaths.FolderPathName FROM PagePaths
INNER JOIN WebsitePaths ON PagePaths.WebsitePathID = WebsitePaths.WebsitePathID
INNER JOIN FolderPaths ON PagePaths.FolderPathID = FolderPaths.FolderPathID

MySQL uses a slightly different FROM statement that automatically rewrites its table into a single letter; in this case the "testing" table is renamed t (but only for this SQL statement). Then instead of referencing the real table name like MS SQL Server (PagePaths.PageID), MySQL references the abbreviation (t.`PageID`). You'll also note that MySQL also wraps the field names in single quotes. After everything I'd heard about how identical SQL across different versions, I was interested to see if these differences were fundamental. It turns out, they aren't. You can reformat this SQL statement following the syntax of MS SQL Server, and it runs just fine. The Microsoft version is produced by Visual Web Developer's built in database query analyzer, the MySQL version is produced by MySQL Query Browser, but they're interchangable in MySQL. I have converted the SQL statement back to MySQL's preferred format for consistency. This is the culture I'm now working in now, and I see no reason to challenge conventions that will allow me to collaborate effectively with MySQL developers.

Let's run this again and see what happens. Success! OK, the $64,000 question is how hard is this to transfer to the production server and what is the work flow?

Deploying MySQL to Production

OK. We finally have all the pieces together to test pushing custom applications up to production with a MySQL database. As a quick test, I created a backup copy of the MySQL database, a .sql file to restore the database from. I then dropped one of the tables on my dev box and refreshed this page to produce an error, e.g. .NET can't find the table. Then I restored the database on the dev box from the .sql file and this page runs again. The procedure is almost ridiculously simple, so I'm as ready to upload everything as I'm going to get.

PageIDWebsitePathNameFolderPathNamePageName
1http://beta.earthchronicle.com/ECBeta/Testing/AspNet/Databases/Database04DataAccessLayerDal.aspx
2http://beta.earthchronicle.com/ECBeta/Testing/AspNet/CSharpProgramming/CSharp02LinkFactory.aspx
3http://www.earthchronicle.com/EC/Volunteers/VolunteerArticleWriters.aspx
4http://beta.earthchronicle.com/ECBeta/Testing/AspNet/CSharpProgramming/CSharp09GenericCollections.aspx

YES!!!!!!!!!!

You're looking at live data from the MySQL server. What you don't know is how slick this all fits into my desired architecture. MySQL literally could not work any better with the iterative development techniques that I've been streamlining for the last year.

I copied the new pages to the production server, direct and easy as always. Then I used MySQL Administrator to create a remote connection to the production instance of MySQL. I was able to "restore" the production database from the .sql file quickly and easily; the custom tables from the dev box database were copied over perfectly. And it was as simple as restoring the MySQL instance on my dev box. I've made no modifications of any kind to the .NET code or the MySQL database, I simply copied everything to production and it works. The only differences, like the connection strings, are conveniently contained in the web.config file where they can be managed as easily as possible.

Let's take a look at my original project requirements. It included getting the MySQL database, .NET Connector, and MySQL GUI Tools up and running; all are performing well. I also specified...

  • I need the same code to work in production and on the dev box. If it doesn't, that would be a deal breaker. While file-based databases (.mdb & .mdf) have their limitations, they are extremely portable, and support this goal. Now, the web.config files will naturally have to be customized to connect to "their" MySQL instance, but if the .cs files require modified formats, I would have to jettison MySQL.
  • I need to be able to use backups to restore either database. For example, I need to be able to backup the database on the dev box and use it to "restore" the production server database. This would allow me to "copy" the database from dev to production and vice versa, just like I copy the website back and forth. If I can't, that would be a deal breaker.

The MySQL database and the C# I've written against it (including the .NET Connector) all work flawlessly without any modification. Once the code is working on the dev box, I literally copy to production where everything seems to function just as well. The database back ups also work as advertised. I can make a back up of either database quickly and easily in MySQL Administrator, and restore either database with the .sql file produced by the back up. By restoring a database with the .sql file from the other MySQL instance I can copy the database as easily as I copy the website, easier actually.

This was a lot of work, but MySQL performs everything I could have hoped for. I now have an enterprise class database at my disposal which is optimized and streamlined to deploy the website including any changes seamlessly into production. While I wouldn't relish having to set this up again, it's up and running, and I shouldn't ever have to worry about it. I can simply build new applications and take MySQL for granted. And now that I've seen how well it works for real development, if my computer were to go down, I would reinstall MySQL in a heartbeat.

Another point I find interesting is that MySQL backup files record dates using the same alphabetical convention used at Earth Chronicle. The .sql file backup which I just created is named "Backup 20091120 1244.sql" where 20091120 stands for 2009 November 20th. This makes perfect sense, since as a DBA for an enterprise class database, you don't have time to go wandering around looking for a particular backup. You need to locate the file in a file explorer quickly and chronological order is the obvious way to do that.

One Last Programming Note

I had considered explaining the code changes from this test in detail, but it's virtually identical to what we did on the last page. Here's the complete code in the Page_Load event handler for this page with changes highlighted in bold.

// Create a connection to the new MySQL database
string mysqlConnectionString = WebConfigurationManager.ConnectionStrings["MysqlSchemaConnectionString"].ConnectionString;
MySqlConnection connectionToTheMysqlDatabase = new MySqlConnection(mysqlConnectionString);

//Create a command object to pass a SQL statement to the MySQL database
string sqlStatementForTheMysqlDatabase = "SELECT t.`PageID`, testingwebsitepaths.`WebsitePathName`, testingfolderpaths.`FolderPathName`, t.`PageName` FROM testing t INNER JOIN testingwebsitepaths ON t.WebsitePathID = testingwebsitepaths.WebsitePathID INNER JOIN testingfolderpaths ON t.FolderPathID = testingfolderpaths.FolderPathID";
MySqlCommand commandForTheMysqlDatabase = new MySqlCommand(sqlStatementForTheMysqlDatabase, connectionToTheMysqlDatabase);

DataTable dataForMysqlDatabaseLiveTestingData = new DataTable();

try
{
commandForTheMysqlDatabase.Connection.Open();
dataForMysqlDatabaseLiveTestingData.Load(commandForTheMysqlDatabase.ExecuteReader());
}
finally
{
commandForTheMysqlDatabase.Connection.Close();
}

htmlTableOfLiveTestingMysqlData.DataSource = dataForMysqlDatabaseLiveTestingData.DefaultView;
htmlTableOfLiveTestingMysqlData.DataBind();

You can see that I changed the SQL statement and the connection string I'm using, by changing the connection name referenced in the Web.config file. Everything else was the same except that I renamed two variables to tie their semantics to how they're used on this page, the DataTable and the id of the GridView.