I looked at the edmx file before and after changing the mode of concurrency properties and came up with a solution as a console application that parses the edmx file, starting with the storage model for timestamp columns, and changing the conceptual model to after mappings. This is a fairly reliable solution, but it has some reservations.
I am using MSSQL 2008 for which timestamp / rowversion is a type of defacto concurrency. In my models, I use this type only as a concurrency token. If you use it elsewhere but use a consistent name for all concurrency tokens instead, I have included code for this script that can be uncommented. If you just use a different type of concurrency, and this type is unique to concurrency columns, you can change the type from "timestamp" in this line of code:
IEnumerable<XElement> storageEntities = from el in ssdl.Descendants(XName.Get("EntityType",ssdlNS)) where (from prop in el.Elements(XName.Get("Property",ssdlNS)) where prop.Attribute("Type").Value == "timestamp" select prop).Count()>0 select el;
If your concurrency template is more complex, this code will not be enough.
Having said that, I freaked out about an hour later and started it, and he did a great job on my model with almost 200 objects, saving me a lot of annoyance going through the languid edmx designer, so I'm sure others will benefit. This is a console application, so you can put it in the pre-build phase of your model project to integrate it into your build process.
using System; using System.Collections.Generic; using System.Linq; using System.Xml; using System.Xml.Linq; namespace ConfigureConcurrency { class Program { static void Main(string[] args) { string edmxPath = args[0]; //or replace with a fixed path if (edmxPath == null || edmxPath.Length == 0) return; string edmxNS = @"http://schemas.microsoft.com/ado/2008/10/edmx"; string ssdlNS = @"http://schemas.microsoft.com/ado/2009/02/edm/ssdl"; string csdlNS = @"http://schemas.microsoft.com/ado/2008/09/edm"; string mapNS = @"http://schemas.microsoft.com/ado/2008/09/mapping/cs"; XElement root = XElement.Load(edmxPath); //Storage Model XElement ssdl = root.Descendants(XName.Get("StorageModels", edmxNS)).FirstOrDefault(); //Conceptual XElement csdl = root.Descendants(XName.Get("ConceptualModels", edmxNS)).FirstOrDefault(); //Mapping XElement map = root.Descendants(XName.Get("Mappings", edmxNS)).FirstOrDefault(); /* Use this code instead of the line below it, if the type of your concurrency columns is used on other non-concurrency columns and you use the same name for every concurrency column string ConcurrencyColumnName = "RowVersion"; IEnumerable<XElement> storageEntities = from el in ssdl.Descendants(XName.Get("EntityType", ssdlNS)) where (from prop in el.Elements(XName.Get("Property", ssdlNS)) where prop.Attribute("Name").Value == ConcurrencyColumnName select prop).Count() > 0 select el; */ IEnumerable<XElement> storageEntities = from el in ssdl.Descendants(XName.Get("EntityType",ssdlNS)) where (from prop in el.Elements(XName.Get("Property",ssdlNS)) where prop.Attribute("Type").Value == "timestamp" select prop).Count()>0 select el; //for each timestamp column, find the mapping then find the conceptual model property and establish the concurrency mode foreach(XElement storageEntity in storageEntities) { //Get the mapping XElement mapping = (from el in map.Descendants(XName.Get("EntityTypeMapping",mapNS)) where el.Element(XName.Get("MappingFragment",mapNS)).Attribute("StoreEntitySet").Value == storageEntity.Attribute("Name").Value select el).FirstOrDefault(); if (mapping != null) { //Get the column mapping XElement column = (from el in storageEntity.Descendants(XName.Get("Property",ssdlNS)) where el.Attribute("Type").Value == "timestamp" select el).FirstOrDefault(); string columnName = column.Attribute("Name").Value; XElement columnMapping = (from el in mapping.Descendants(XName.Get("ScalarProperty",mapNS)) where el.Attribute("ColumnName").Value == columnName select el).FirstOrDefault(); string propertyName = columnMapping.Attribute("Name").Value; //Get the conceptual schema namespace and type name string[] split = mapping.Attribute("TypeName").Value.Split('.'); string ns="", typeName =split[split.Length-1]; for (int i = 0; i < split.Length-1; i++) { if (i>0) ns+="."; ns += split[i]; } //Find the entry in the conceptual model XElement schema = (from el in csdl.Elements(XName.Get("Schema",csdlNS)) where el.Attribute("Namespace").Value == ns select el).FirstOrDefault(); if (schema != null) { //Find the entity type XElement entity = (from el in schema.Descendants(XName.Get("EntityType",csdlNS)) where el.Attribute("Name").Value == typeName select el).FirstOrDefault(); //Find the property XElement concurrencyProperty = (from el in entity.Elements(XName.Get("Property",csdlNS)) where el.Attribute("Name").Value == propertyName select el).FirstOrDefault(); //Set concurrency mode to fixed concurrencyProperty.SetAttributeValue("ConcurrencyMode", "Fixed"); } } } //Save the modifications root.Save(edmxPath); } } }