Published Thursday, December 20, 2007 9:26 AM by mtaulty

LINQ to SQL - Tailoring the Mapping at Runtime

Mats has a post over here about concurrency in LINQ to SQL with a long write-up about issues that he sees in using the framework.

I've not read all of it yet because the very first point that he raises sent me off on a bit of a tangent (which does not necessarily provide an answer to any/all of his post - it's just something that it caused me to think about).

The first point is - in the case where you (for whatever reason) don't want optimistic concurrency with LINQ to SQL, how would you turn it off globally?

As far as I know there's no "override" flag that you can set anywhere to just switch off concurrency checking and (given that the default value for UpdateCheck is Always) you're going to have to visit all the "Column attributes" somehow and tweak that value.

LINQ to SQL doesn't offer you the opportunity to customise the code-generation process like the Entity Framework does so there's not much you can do there AFAIK.

So, what are the options for tweaking a mapping using the UpdateCheck flag as a particular example?

Some thoughts I had (not necessarily exhaustive or correct :-));

Build-Time 1

  1. Use sqlmetal.exe. Extract a .dbml file from your database.
  2. Use some tool to modify the .dbml file in order that all <Column/> elements have an UpdateCheck="Never" attribute on them.
  3. Use sqlmetal.exe again to generate your attributed mapping code from the .dbml file.

Build-Time 2

  1. Use the designer.
  2. Visit all the entities on your model, select all their properties, use the properties window to change UpdateCheck to Never.

Build-Time 3

  1. Use sqlmetal.exe or the designer to extract code from your database.
  2. Use some tool to modify the code file and change the [Column] attributes to have UpdateCheck=Never on them.

Build-Time 4

  1. Use sqlmetal.exe to extract code and an XML mapping from your database.
  2. Use some tool to modify the XML mapping in order that all <Column/> elements have an UpdateCheck="Never" attribute on them.

None of these is particularly pretty so I then started to wonder whether it might be better to do it at runtime.

When you map with LINQ to SQL you're either feeding the framework mapping information in the form of a AttributeMappingSource working from code attributes or an XmlMappingSource working from an XML file.

Either way, you end up with a bunch of metadata represented by a MetaModel with a bunch of MetaTypes which has a property HasUpdateCheck which returns true/false based on whether any of the "columns" of that type has an UpdateCheck which is not set to Never.

What'd be nice would be if you could just "tweak" the MetaModel that you're using to modify values such as HasUpdateCheck but the relevant classes/extension points don't look to be there to make that happen.

So, I was wondering how feasible it was to do

Run-Time 1

  1. Use sqlmetal.exe. Generate code (unattributed) and an XML mapping file.
  2. At runtime, modify the XML mapping before handing it to the DataContext.

To try this out (on Northwind) I did;

  1. sqlmetal.exe /server:. /database:northwind /code:northwind.cs /map:northwind.xml
  2. Added northwind.cs and northwind.xml to a console application project.
  3. Wrote a bit of code as below.
    static XmlMappingSource LoadModifiedMapping(string mappingFile,
      IDictionary<string,string> attributesToModify)
    {
      XElement mapping = XElement.Load(mappingFile);

      foreach (var column in mapping.Descendants(
        "{http://schemas.microsoft.com/linqtosql/mapping/2007}Column"))
      {
        foreach (string key in attributesToModify.Keys)
        {
          column.SetAttributeValue(key, attributesToModify[key]);
        }
      }
      XmlMappingSource source = null;

      using (XmlReader reader = mapping.CreateReader())
      {
        source = XmlMappingSource.FromReader(reader);
        reader.Close();
      }
      return (source);
    }
    static void Main(string[] args)
    {
      Dictionary<string, string> attributes = new Dictionary<string, string>();
      attributes["UpdateCheck"] = "Never";

      XmlMappingSource source = LoadModifiedMapping("northwind.xml",
        attributes);

      using (Northwind ctx = new Northwind(
        "server=.;database=northwind;integrated security=sspi", source))
      {
        var query = from c in ctx.Customers
                    where c.Country == "Spain"
                    select c;

        foreach (Customers c in query)
        {
          c.Country = "UK";
        }
        ctx.SubmitChanges(); // We should see no concurrency checks here. Very short WHERE clauses.
      }
    }

So, here we're using the code-gen process to give us some entity classes to play with but those classes are unattributed with mapping information. The mapping information is in an XML file. We load that XMl file, change a few attributes on it and then hand it over to the framework as the basis of mapping.

I thought it was "interesting" enough to share - if there's a better way of altering the mapping process then feel free to let me know and I'll update the post here.


Filed Under: , ,

# Link Listing - December 20, 2007 @ Friday, December 21, 2007 5:14 AM

ASP.NET  How-To: New ASP.NET 3.5 Extensions Video Screencasts [Via: Scott Hanselman ] Link Blogs  Links...

Christopher Steen

# Link Listing - December 20, 2007 @ Friday, December 21, 2007 5:14 AM

Link Listing - December 20, 2007

Christopher Steen

# The Daily Find #16 | TechToolBlog @ Friday, December 21, 2007 8:59 AM

PingBack from http://www.techtoolblog.com/archives/the-daily-find-16

The Daily Find #16 | TechToolBlog