2010-08-30 27 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