2010-08-30 49 views
2

我想在不修改文件本身的情況下對EF4 edmx文件進行一些更改,主要是如果從數據庫重新生成模型,我不會丟失所有更改。我對XSL很熟悉,並且看到了將它與edmx文件結合使用的參考資料。這聽起來像是一個很好的解決方案,但是我似乎無法找到任何有關如何實際設置的文檔。你是從edmx文件引用樣式表,還是將其配置爲查看模板,然後以某種方式加載edmx文件?任何資源在此讚賞。使用XSLT轉換實體框架EDMX文件

澄清:

具體是什麼,我試圖做的是修改模型,使一些意見作爲表與模型內部的關係,在這裏看到:http://blogs.msdn.com/b/alexj/archive/2009/09/01/tip-34-how-to-work-with-updatable-views.aspx

的問題,我會有使用該方法是如果我需要更新數據庫並重新生成模型,我將不得不返回並再次進行所有這些更改,我希望有一種方法可以使用xslt對視圖進行更改當模型重新生成時,它們不會被刪除。

+0

聽起來好像問題的一半是「更新模型」嚮導的工作方式;通過替換SSDL並覆蓋更改。如果您想要更具選擇性的方式更新模型(只更新要更新的部分),請查看EF4的模型比較器。你可以在這裏看到一個介紹截屏:http://huagati.blogspot.com/2010/07/introducing-model-comparer-for-entity.html ...並下載它+從http:// www獲得試用許可證.huagati.com/dbmltools/ – KristoferA 2010-08-31 06:36:26

回答

4

「這是很難說什麼是被要求在這裏」;)

你說的意思是什麼「做出一些改變我的EF4 EDMX文件而不修改文件本身」。你想從原始創建派生edmx?如果是這樣,您需要注意在保存期間自動生成C#代碼(=類定義)。

我從事EF項目,並使用XSLT後處理edmx文件和/或生成其他代碼。這是在構建期間手動或從批處理文件調用的。

您可以使用.Net框架從簡單的Powershell script調用XSLT。 My blog posts on EF(3.5)可以幫助您理解edmx處理。

+0

感謝您的回覆,試圖澄清以上內容。 – 2010-08-30 18:24:06

0

我對EF4本身一無所知,但是怎麼樣:假設您的原始edmx文件(從db重新生成)是「A.edmx」。當您給EF4指定edmx文件的名稱時,給它一個URL(如果允許)「http://localhost/B.edmx」。設置一個簡單的Web服務(我不是指SOAP,而是簡單的XML),它會響應這個URL,並用XSLT樣式錶轉換A.edmx。

或者,避免Web服務部分,並讓您的應用程序檢查B.edmx對A.edmx的時間戳;如果A更新,或者B不存在,讓它運行XSLT處理器以將A.edmx轉換爲B.edmx。

HTH。如果這沒有幫助,請提供一些更具體的信息。

+0

感謝您的迴應我試圖澄清我想要做的上面。 – 2010-08-30 18:24:31

4

我意識到這是有點過時了,但我最近找到了一個解決方案來轉換一個Edmx的保存,我想我會分享。請注意,我們使用的是Visual Studio 2012,Entity Framework 6.0和.Net 4.5。我們沒有使用Code First。

我們的問題是,通過實體框架生成的視圖有我們不想要的額外主鍵列(而不是我們需要的列)。我們無法在視圖上創建唯一性約束,因爲視圖引用了非確定性函數。所以這就意味着要更正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.在Transaction_Detail_Base上添加ConcurrencyFixed到版本字段;和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> 

最後,這裏有一個用於在構建時驗證.edmx的Powershell腳本示例。

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