Sunday, August 15, 2010

Microsoft.Data is so wrong!

Their was some rumor in the last days about microsoft addressing a developer type called "Mort" with a new Data Layer called Microsoft.Data. Mort is described by Nikhil Kothari's blog post as

Mort, the opportunistic developer, likes to create quick-working solutions for immediate problems and focuses on productivity and learn as needed. Elvis, the pragmatic programmer, likes to create long-lasting solutions addressing the problem domain, and learn while working on the solution. Einstein, the paranoid programmer, likes to create the most efficient solution to a given problem, and typically learn in advance before working on the solution. In a way, these personas have helped guide the design of features during the Whidbey product cycle.


While their were voices that addressing the Mort developer type is just plain wrong I support the idea to address Mort's way to develop on the .NET platform but I can't support the how microsoft tries to achieve this.

The how is just plain wrong! If you read the original announcement of Microsoft.Data David Fowler outlines the "pro" of Microsoft.Data by comparing these two code snippets:

using (var connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Northwind.mdf;Initial Catalog=|DataDirectory|\Northwind.mdf;Integrated Security=True;User Instance=True")) {
  using (var command = new SqlCommand("select * from products where UnitsInStock < 20", connection)) {
    connection.Open();
    using (SqlDataReader reader = command.ExecuteReader()) {
      while (reader.Read()) {
        Response.Write(reader["ProductName"] + " " + reader["UnitsInStock"]);
      }
    }
  }
}
Compared to the Microsoft.Data approach:
using (var db = Database.OpenFile("Northwind")) {
  foreach (var product in db.Query("select * from products where UnitsInStock < @0", 20)) {
    Response.Write(product.ProductName + " " + product.UnitsInStock);
  }
}

The "mental approach" of both code snippets does not fit Mort's needs! The assumption of both code snippets is: "If we want to access or store data we need to do that using SQL"! This assumption is just plain wrong!

Why bother Mort with SQL? Why should Mort learn how to prevent SQL Injections? Why should Mort be concerned about connection strings? Transactions? Why should be Mort concerned about Data modelling? And again - Why should Mort need to learn yet another language (SQL) to do just the simplest data access?

It's not the case that SQL is the best approach to store and access data objects! If we take a look at mongoDB with a mongoDB driver like NoRM or RavenDB we do not need SQL, we do not need to worry how to persist our object in an SQL server!

What I want as Mort is code like that (stolen from RavenDB tutorials)
using (var session = store.OpenSession())
{
    var order = session.Load<Order>("orders/1");
    Console.WriteLine("Customer: {0}", order.Customer);
    foreach (var orderLine in order.OrderLines)
    {
        Console.WriteLine("Product: {0} x {1}", orderLine.ProductId, orderLine.Quantity);
    }
    session.SaveChanges();
}

Dear Microsoft, don't try to hide the complexity dealing with a relational database and get the mental shift towards some (NoSQL) alternatives! And Mort will follow!

Saturday, August 7, 2010

SubSonic Custom Property Mapping Attributes

Some commits ago we opened SubSonic's property mapping attribute implementation to allow developers to extend how properties are mapped to database columns when using SimpleRepository with migrations.
In this post I'll show you how to implement a custom property mapping attribute that declares a property to be mapped to a database column of type "xml" instead of just a plain nvarchar.

Custom property mapping attributes have to inherit (obviously) from Attribute and implement the IPropertyMappingAttribute interface from namespace SubSonic.SqlGeneration.Schema.

IPropertyMappingAttribute defines the methods Accept and Apply where Accept defines if the Apply method should be invoked for this property. For our implementation we only want to accept properties of type "string". In our apply method we get an IColumn instance passed that we can modify. We can access the Table and Provider property through that IColumn instance if we need it. For our simple implementation this is not needed.

So our final implementation looks like this

public class SubSonicXmlStringAttribute : Attribute, IPropertyMappingAttribute
{
  public bool Accept(IColumn column)
  {
    return column.IsString;
  }

  public void Apply(IColumn column)
  {
    column.DataType = DbType.Xml;
  }
}

If you're using SimpleRepository with auto migratons you'll get every property marked with SubSonicXmlStringAttribute mapped to a database column of type xml. That was easy!