Linq to SQL DBML TFS Merge Strategies

I know linq is a somewhat outdated framework but it’s super simple to use and still gets the job done for us. It’s been used for several years on our current project and we have always run into some issues when merging between 2 branches. The DBML and designer files would get all out of wack during the merge process and would require a lot of tweaking to make correct.

We created a project called “DBML Organizer” that we would run on the source .DBML and .layout files that would sort the XML alphabetically. From there the TFS merge logic would handle pretty much all cases.

The following code can be copied and pasted and used as is.

[code language=”csharp”]
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace DbmlOrganizer
{
class Program
{
static void Main(string[] args)
{
var path = args.Length > 0 ? args[0] : @"path to some folder with the .dbml files";

Directory.GetFiles(path, "*.dbml", SearchOption.TopDirectoryOnly).ToList().ForEach(f => FormatDbml(f));
Directory.GetFiles(path, "*.dbml.layout", SearchOption.TopDirectoryOnly).ToList().ForEach(f => FormatDbmlLayout(f));

Console.WriteLine("any key to continue");
Console.ReadKey();
}

private static void FormatDbmlLayout(string layoutFile)
{
var xml = XElement.Load(layoutFile);

var classShapes = (from cs in xml.Descendants("classShape")
orderby cs.Descendants("DataClassMoniker").First().Attribute("Name").Value
select cs).ToList();
classShapes.ForEach(cs => cs.Remove());

var nestedChildShapes = xml.Descendants("nestedChildShapes").FirstOrDefault();
if (nestedChildShapes == null)
return;

nestedChildShapes.Add(classShapes);

if (!File.GetAttributes(layoutFile).HasFlag(FileAttributes.ReadOnly))
{
xml.Save(layoutFile, SaveOptions.None);
Console.WriteLine("Processed Layout- " + layoutFile);
}
else
{
Console.WriteLine("Layout ignored because of read-only- " + layoutFile);
}
}

private static void FormatDbml(string dbmlFile)
{
Action<XNamespace, XElement> tableFormatter = (ns2, tableXml) => {
var type = tableXml.Descendants(ns2 + "Type").FirstOrDefault();

var columns = SortXml(ns2, tableXml, "Column", "Name").ToList();
type.Add(columns);
};

XNamespace ns = "http://schemas.microsoft.com/linqtosql/dbml/2007";

var xml = XElement.Load(dbmlFile);

var connection = xml.Descendants(ns + "Connection").FirstOrDefault();

var tables = SortXml(ns, xml, "Table", "Name").ToList();
tables.ForEach(t => tableFormatter(ns, t));

var functions = SortXml(ns, xml, "Function", "Name").ToList();

// don’t change the order of the function params, etc.
//functions.ForEach(f => FormatDbmlFunction(ns, f));

connection.AddAfterSelf(functions);
connection.AddAfterSelf(tables);

if (!File.GetAttributes(dbmlFile).HasFlag(FileAttributes.ReadOnly))
{
xml.Save(dbmlFile, SaveOptions.None);
Console.WriteLine("Processed DBML – " + dbmlFile);
}
else
{
Console.WriteLine("DBML ignored because of read-only- " + dbmlFile);
}
}

private static IEnumerable<XElement> SortXml(XNamespace ns, XElement parent, string elementName, string attributeName)
{
var elements = (from c in parent.Descendants(ns + elementName) orderby c.Attribute(attributeName).Value ascending select c).ToList();
elements.ForEach(e => e.Remove());
return elements;
}

}
}
[/code]