Thursday, December 22, 2011

ASP.NET MVC 3: How to update EdmMetadata ModelHash when manually adding a field to a database

Having developed web applications for 8 years now, I've gotten very used to adding fields to existing web applications. It is not an everyday thing. Maybe not even a every month thing. But it is definitely a every major release thing. With that in mind, Microsoft's MVC 3 seems a bit incomplete in what they are recommending you do every time you need to add a field to a existing MVC web application.

Option 1: Microsoft's suggested solution? Add a few lines of code that will automatically drop the table, re-create it, then seed it through an Initializer class if it detects that the schema has changed. This may be fine for development, but since most people actually looking to do this is working on a production web applications, this is a bad idea even to propose as one of the ways to add a field to your web application. And you must be extra careful when implementing this on a simple table. Imagine you or someone updated the schema of a table that has a lot of valuable data, but you just want to update a few tables with a few rows of data in it and you could easily seed it. You turn this feature on and that valuable data vanishes without a trace for that customer.

Option 2: If you try to search for the solution, the number one recommended solution is to disable checking of changes to the schema. This isn't a bad suggestion. Just turn the whole darn thing off. You and maybe a handful of people are care about schema changes anyway. Why have this elaborate schema checking system in place anyway? The answer is that the customers have compliance requirements, and they are required that their web application meets those requirements. And a common one is that they be notified of schema changes to their database.

Better Solution: What MVC does is that it runs a consistency check by hashing the schema definition. And they don't provide a well documented way to update this hash value. What they should do is let the user put the database in a learning or training mode. So that if the schema changes, it will update the stored hash value to the new value. Then when you are done adding the field, you can switch off he learning mode so that it throws up the normal server error when someone has maliciously (or unknowingly) changed the schema without alerting the right people. So this is my solution to make all that happen:

Step 1: Open Global.asax.cs in your MVC web application.
Step 2: Add these lines to the top of the file:
using myMvcProjectName.Models;
using System.Data.SqlClient;

Step 3: Add these lines to the main class for your MVC web application (the class is probably named MvcApplication).
private myDBContext db = new myDBContext();
public void Update_EdmMetadata_ModelHash()
{
string newHash = System.Data.Entity.Infrastructure.EdmMetadata.TryGetModelHash(db);

SqlConnection sqlConn = new SqlConnection(myConnectionString);
SqlCommand sqlComm = new SqlCommand("UPDATE EdmMetadata SET ModelHash = '" + newHash + "'", sqlConn);
sqlConn.Open();
sqlComm.ExecuteNonQuery();
sqlConn.Close();
}

Step 4: Add this line to the top of Application_Start, to run the new function to turn on "learning" mode. Just comment it out when you are done:
Update_EdmMetadata_ModelHash();

Italic = change these values accordingly.

3 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Can you post what connection string you're using? I tried using the connection string in my web.config file and it doesn't seem to work (see bottom). I'm following the following a tutorial (http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/adding-a-model) and that's what it recommended my connection string should be in the web.config file, but when I use it in your example and run the web app on my localhost, I get the run-time error "Invalid object name 'EdmMetadata'."

    The connection string I used is:
    "data source=.\\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"

    ReplyDelete