Automating ConcurrencyMode set to Fix in EDMX

此生再无相见时 提交于 2019-12-22 11:33:50

问题


Majority of our tables in our models has a field called "intConcurrencyID" that we intended to use for concurrency checking. I believe the only way to enable this field for concurrency checking in a Database First environment is to set the Concurrency Mode to Fixed.

However, if we have huge numbers of tables per EDMX, we will have a hard time manually configuring each type per entity not to mention the possibility of some entities to be overlooked.

Do you have any ideas on how I can automate this process? It seems that T4 Template is not the way to go since the Concurrency Mode is in the CSDL not in the Code Behind..


回答1:


I looked at the edmx file before and after changing a property's concurrency mode and came up with a solution as a console app which parses the edmx file starting with the storage model for 'timestamp' columns and modifies the conceptual model by following the mappings. This is a fairly robust solution, but it has some caveats.

I'm using MSSQL 2008, for which the timestamp/rowversion is the defacto concurrency type. In my models I only use this type as a concurrency token. If you use it in other places, but have a consistent name used for all concurrency tokens instead, I've included code for this scenario that can be uncommented. If you simply use a different concurrency type and that 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 pattern is more complicated, this code won't suffice.

That being said, I whipped this up in about an hour and ran it and it worked great for me on a model with nearly 200 entities, saving me a lot of annoyance going through the sluggish edmx designer, so I'm sure others will benefit. This is a console application so you can put this in the prebuild step 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);


        }
    }
}



回答2:


Solved my similar problem with the above console app. But if you are running a later version of Entity Framework, be sure to update references to schemas. I am using EF 5 and for me I used the following lines:

string edmxNS = @"http://schemas.microsoft.com/ado/2009/11/edmx";
string ssdlNS = @"http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
string csdlNS = @"http://schemas.microsoft.com/ado/2009/11/edm";
string mapNS = @"http://schemas.microsoft.com/ado/2009/11/mapping/cs";


来源:https://stackoverflow.com/questions/11980516/automating-concurrencymode-set-to-fix-in-edmx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!