代码之家  ›  专栏  ›  技术社区  ›  Graham Conzett

用xslt转换实体框架edmx文件

  •  2
  • Graham Conzett  · 技术社区  · 14 年前

    我希望在不修改文件本身的情况下对EF4 EDMX文件进行一些更改,主要是这样,如果我从数据库重新生成模型,就不会丢失所有更改。我熟悉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 回复  |  直到 11 年前
        1
  •  4
  •   devio    14 年前

    “很难说这里有什么问题”;)

    您所说的“在不修改文件本身的情况下对EF4 EDMX文件进行一些更改”是什么意思?是否要从原始文件创建派生edmx?如果是这样,您需要知道C代码(=类定义)是在保存期间自动生成的。

    我在EF项目上工作,并使用XSLT来后期处理EDMX文件和/或生成附加代码。在生成期间,可以手动或从批处理文件调用此函数。

    您可以从一个简单的 Powershell script 使用.NET框架。 My blog posts on EF (3.5)可能有助于您理解EDMX处理。

        2
  •  4
  •   Craig B    11 年前

    我意识到这有点过时了,但最近我发现了一种解决方案,可以通过保存来转换EDMX,我想我会分享它。请注意,我们使用的是Visual Studio 2012、Entity Framework 6.0和.NET 4.5。我们没有先使用代码。

    我们的问题是,通过实体框架生成的视图具有我们不想要的附加主键列(而不是我们需要的列)。我们无法在视图上创建唯一性约束,因为视图引用了一个非确定性函数。因此,这意味着获得正确视图键列的唯一方法是更新EDMX文件。

    为了实现这一点,我更新了T4模板以加载EDMX文件,使用XSLT对其进行翻译,然后再次保存。这意味着每当开发人员从Designer窗口保存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添加到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
    
        3
  •  0
  •   LarsH    14 年前

    我对EF4本身一无所知,但如何处理:假设您的原始EDMX文件(从数据库重新生成)是“a.edmx”。当您给EF4提供EDMX文件的名称时,请给它一个URL(如果允许的话)“ http://localhost/B.edmx “。设置一个简单的Web服务(我不是指SOAP,而是简单的XML),通过使用XSLT样式表转换.edmx来响应这个URL。

    或者,避免使用Web服务部件,让您的应用程序对照a.edmx检查b.edmx的时间戳;如果a较新,或者b不存在,则让它运行一个XSLT处理器,将a.edmx转换为b.edmx。

    Hth.如果没有帮助,请给出更多细节。