Я понимаю, что это немного устарело, но недавно я нашел решение для преобразования Edmx в Save, которым я поделился. Обратите внимание, что мы используем Visual Studio 2012, Entity Framework 6.0 и .Net 4.5. Мы не используем Code First.
Наша проблема заключалась в том, что представления, созданные с помощью Entity Framework, имели дополнительные столбцы первичного ключа, которые нам не нужны (вместо того, чтобы не иметь необходимых нам столбцов). Мы не могли создать ограничения уникальности для Представлений, потому что представления ссылались на недетерминированную функцию. Таким образом, это означало, что единственный способ получить правильные столбцы ключа View - обновить файл edmx.
Для достижения этой цели я обновил шаблон T4, чтобы загрузить файл edmx, перевести его с помощью xslt и снова сохранить. Это означает, что каждый раз, когда разработчик сохраняет файл edmx из окна конструктора, он будет корректно обновляться перед созданием класса .cs. В качестве дополнительной проверки я также создал сценарий powershell для проверки первичных ключей во время наших автоматических сборок.
Вот пример кода (внутри нашего файла Model1.tt вверху).
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@ output extension=".cs"#>
<#@ assembly name="System.Xml" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Xsl" #>
<#
XmlDocument rawXDoc = new XmlDocument();
XmlDocument xDoc = new XmlDocument();
XmlReaderSettings settings = new XmlReaderSettings {
//ConformanceLevel = ConformanceLevel.Document;
DtdProcessing = DtdProcessing.Prohibit
};
//Note that to use the Host.ResolvePath below you must set hostspecific="true" in the template directive.
using (FileStream rawDocFileSteam = File.OpenRead(Host.ResolvePath("MyDataModel.edmx"))) {
using (XmlReader rawDocReader = XmlReader.Create(rawDocFileSteam, settings)) {
using (XmlTextReader xsltReader = new XmlTextReader(Host.ResolvePath("DataModelTransform.xslt")) ) {
XslCompiledTransform xsltTransform = new XslCompiledTransform();
xsltTransform.Load(xsltReader); //Ensure the XML Resolver is null, or a XmlSecureResolver to prevent a Billion Laughs denial of service.
using (MemoryStream ms = new MemoryStream()) {
xsltTransform.Transform(rawDocReader, null, ms);
ms.Position = 0;
xDoc.Load(ms);
}
}
}
}
xDoc.Save(Host.ResolvePath("MyDataModel.edmx"));
#>
Вот пример файла xslt, который мы используем. Это делает две вещи. 1. Добавляет ConcurrencyFixed в поле Version в Transaction_Detail_Base; и 2. удаляет недопустимые столбцы первичного ключа.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ssdl="http://schemas.microsoft.com/ado/2009/11/edm/ssdl"
xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx"
xmlns:edm="http://schemas.microsoft.com/ado/2009/11/edm"
xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
>
<xsl:output method="xml" indent="yes"/>
<!--Ensure blank lines aren't left when we remove invalid PrimaryKey fields.-->
<xsl:strip-space elements="*"/>
<xsl:template match="@*|*|processing-instruction()|comment()">
<xsl:call-template name="CopyDetails"/>
</xsl:template>
<xsl:template name="CopyDetails">
<xsl:copy>
<xsl:apply-templates select="@*|*|text()|processing-instruction()|comment()"/>
</xsl:copy>
</xsl:template>
<!--Set concurrency mode to fixed for Transaction_Detail_Base.-->
<xsl:template match="edmx:ConceptualModels/edm:Schema/edm:EntityType[@Name='Transaction_Detail_Base']/edm:Property[@Name='Version']">
<xsl:call-template name="AddConcurrencyAttribute"/>
</xsl:template>
<!-- Add the ConcurrencyAttribute if it doesn't exist, otherwise update it if it does -->
<xsl:template name="AddConcurrencyAttribute">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
<xsl:attribute name="ConcurrencyMode">Fixed</xsl:attribute>
</xsl:copy>
</xsl:template>
<!-- Remove unused primary key columns from views. Should be removed from StorageMode and ConceptualModels -->
<!--Transaction_Detail. ssdl is the StorageModel section, edm is the ConceptualModel section-->
<xsl:template match="ssdl:EntityType[@Name='Transaction_Detail']/ssdl:Key/ssdl:PropertyRef | edm:EntityType[@Name='Transaction_Detail']/edm:Key/edm:PropertyRef">
<xsl:if test="@Name='Asset' or @Name='Date' or @Name='Portfolio' or @Name='System_Reference'">
<xsl:call-template name="CopyDetails"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Наконец, вот пример скрипта Powershell, используемого для проверки .edmx при сборке.
function IsValidViewNode([string]$viewName, [string[]]$keyFields, [int]$typeCheck)
{
[System.Xml.XmlNodeList]$nodelist = $null;
if ( $typeCheck -eq 1 ) {
$nodelist = $Xml.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:StorageModels/ssdl:Schema/ssdl:EntityType[@Name='$viewName']/ssdl:Key/ssdl:PropertyRef", $nsmgr)
} else
{
$nodelist = $Xml.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/edm:Schema/edm:EntityType[@Name='$viewName']/edm:Key/edm:PropertyRef", $nsmgr)
}
[int] $matchedItems = 0
[int] $unmatchedItems = 0
if ($nodelist -eq $null -or $nodelist.Count -eq 0)
{
return $false;
}
foreach ($node in $nodelist) {
$name = ""
if ($node -ne $null -and $node.Attributes -ne $null -and $node.Attributes -contains "Name" -ne $null )
{
$name = $node.Name
}
#Write-Host $name
if ($keyFields -contains $name) {
$matchedItems++
}
else {
$unmatchedItems++
}
#Write-Host $matchedItems
#Write-Host $unmatchedItems
#Write-Host $keyFields.Length
}
#Right Pad the detail string.,
$resultString = "Primary Keys for $viewName" + (" " * (50 - "Primary Keys for $viewName".Length))
if ( $matchedItems -eq $keyFields.Length -and $unmatchedItems -eq 0 ) {
Write-Host $resultString - Valid
return ""
}
else {
Write-Host $resultString - INVALID
return "$viewName,"
}
}
[string]$PKErrors = ""
# Read the xml file
$xml = [xml](Get-Content 'RALPHDataModel.edmx')
$nsmgr = new-object Xml.XmlNamespaceManager($Xml.NameTable)
$nsmgr.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2009/11/edmx")
$nsmgr.AddNamespace("ssdl", "http://schemas.microsoft.com/ado/2009/11/edm/ssdl")
$nsmgr.AddNamespace("edm", "http://schemas.microsoft.com/ado/2009/11/edm")
<#
***
*** VERIFY PRIMARY KEY COLUMNS FOR VIEWS ***
*** This ensures the developer has run the DataModel.xslt to fix up the .edmx file.
***
#>
$PKErrors = $PKErrors + (IsValidViewNode "Transaction_Detail" ("Asset","Date","Portfolio","System_Reference") 1)
$ExitCode = 0
if ($PKErrors -ne "" ) {
Write-Host "Invalid Primary Keys for Views: " + $PKErrors.TrimEnd(",")
$ExitCode = 100
}
Exit $ExitCode