我想在不修改文件本身的情况下对我的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对视图进行这些更改,以便在重新生成模型时不会删除它们。
答案 0 :(得分:4)
“很难说这里有什么问题”;)
你是什么意思“在不修改文件本身的情况下对我的EF4 edmx文件进行一些更改”。您想从原始创建派生的edmx吗?如果是这样,您需要注意在保存期间自动生成C#代码(=类定义)。
我参与了EF项目,并使用XSLT对edmx文件进行后期处理和/或生成其他代码。这可以在构建期间手动调用或从批处理文件调用。
您可以使用.Net框架从简单的Powershell script调用XSLT。 My blog posts on EF(3.5)可以帮助您理解edmx处理。
答案 1 :(得分:4)
我意识到这有点过时了,但我最近找到了一个解决方案来改造Edmx on Save,我认为我会分享。请注意,我们使用的是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添加到Version字段;和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
答案 2 :(得分: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。如果这没有帮助,请提供更多细节。