Преобразование EDMX-файла Entity Framework с помощью XSLT - PullRequest
2 голосов
/ 30 августа 2010

Я хотел бы внести некоторые изменения в мой файл edmx EF4 без изменения самого файла, главным образом, чтобы не потерять все свои изменения, если я регенерирую модель из базы данных.Я знаком с XSL и видел ссылки на его использование в сочетании с файлом edmx.Это звучит как отличное решение, однако я не могу найти никакой документации о том, как на самом деле настроить это.Вы ссылаетесь на таблицу стилей из файла edmx или вы настраиваете ее на просмотр шаблона, а затем каким-то образом загружаете файл edmx?Любые ресурсы по этому поводу приветствуются.

Пояснение:

В частности, я пытаюсь изменить модель так, чтобы несколько представлений действовали как таблицы с отношениями внутри модели, см. Здесь: http://blogs.msdn.com/b/alexj/archive/2009/09/01/tip-34-how-to-work-with-updatable-views.aspx

Проблема, с которой я столкнусь при использовании этого метода, заключается в том, что если мне нужно обновить базу данных и восстановить модель, мне придется вернуться и снова внести все эти изменения, я надеялсябыл способ использовать xslt для внесения этих изменений в представления, чтобы они не были удалены при регенерации модели.

Ответы [ 3 ]

4 голосов
/ 30 августа 2010

«Трудно сказать, о чем здесь спрашивают»;)

Что вы подразумеваете под «внесением некоторых изменений в мой файл edmx EF4 без изменения самого файла».Хотите создать производный edmx из оригинала?Если это так, вы должны знать, что код C # (= определения классов) автоматически генерируется во время сохранения.

Я работал над проектами EF и использовал XSLT для постобработки файлов edmx и / или создания дополнительного кода.Это было вызвано либо вручную, либо из командного файла во время сборки.

Вы можете вызвать XSLT из простого сценария Powershell , используя .Net Framework. Мои сообщения в блоге на EF (3.5) могут помочь вам понять обработку edmx.

4 голосов
/ 31 января 2014

Я понимаю, что это немного устарело, но недавно я нашел решение для преобразования 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
0 голосов
/ 30 августа 2010

Я ничего не знаю о самом EF4, но как насчет этого: Предположим, ваш исходный файл edmx (сгенерированный из db) - "A.edmx".Когда вы даете EF4 имя файла edmx, присвойте ему URL (если разрешено) "http://localhost/B.edmx". Настройка простого веб-сервиса (я имею в виду не SOAP, а простой XML), который отвечает на этот URL с помощьюрезультат преобразования A.edmx с вашей таблицей стилей XSLT.

В качестве альтернативы, избегайте части веб-сервиса и попросите ваше приложение проверить временную метку B.edmx относительно A.edmx, если A новее, или B нетсуществует, пусть он запускает XSLT-процессор для преобразования A.edmx в B.edmx.

HTH. Если это не поможет, пожалуйста, дайте больше подробностей.

...