问题描述
我们的模型中的大多数表都有一个名为intConcurrencyID的字段,我们打算用于并发检查。我相信在Database First环境中启用此字段进行并发检查的唯一方法是将并发模式设置为Fixed。
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.
但是,如果我们有大量的表每个EDMX,我们将很难手动配置每个实体的每个类型,更不用说一些实体被忽略的可能性。
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.
你对如何自动化这个过程有什么想法吗?看来T4模板不是去的方式,因为并发模式是在CSDL不在代码背后..
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..
推荐答案
我在改变属性的并发模式之前和之后看过edmx文件,并提出了一个解决方案作为控制台应用程序,它从timestamp列的存储模型开始解析edmx文件,并通过跟踪映射来修改概念模型。这是一个相当健壮的解决方案,但它有一些警告。
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.
我使用MSSQL 2008,其中timestamp / rowversion是defacto并发类型。在我的模型中,我只使用这种类型作为并发令牌。如果在其他地方使用它,但是具有用于所有并发令牌的一致名称,我已经包括了可以取消注释的此场景的代码。如果只是使用不同的并发类型,并且该类型对并发列是唯一的,那么您可以在这行代码中更改timestamp的类型:
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.
话虽如此,我在大约一个小时的时间里打开了它,并运行它,它对我有一个具有近200个实体的模型很好,为我节省了很多烦恼通过怠惰edmx设计师,所以我相信其他人会受益。这是一个控制台应用程序,因此您可以将其放在模型项目的预构建步骤中,以将其集成到您的构建过程中。
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);
}
}
}
这篇关于在EDMX中将自动并发模式设置为修复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!