什么T4文件用于通过“从数据库更新模型”从数据库生成EDMX?
问题描述:
使用EF4(edmx)模型时,我们经常需要处理“从数据库更新模型”。通常,我们需要删除表并让它们从数据库中完全重新生成。什么T4文件用于通过“从数据库更新模型”从数据库生成EDMX?
手头的问题是我们有多个递归关系/属性。默认情况下,“从数据库更新模型”流程会创建包含对象名称的属性,然后为每个附加关系添加1,2,3等。因此,如果我有多次指向自己的“公司”表(如母公司和dba公司),则当前edmx结果将显示在Company1和Company2中。我需要控制他们的命名....不是手动的。
如果我能找到T4文件(或拦截和控制)edmx文件本身的代码,我可以解决这个问题。
答
只是在寻找别的东西时偶然发现了这个问题,所以我希望你自己解决了这个问题。一段时间后,我和你有完全相同的问题。我得到的方法是使用EDMX.tt“预洗”T4模板,它在EDMX文件中重新命名了这些属性。唯一的皱纹是要记住保存EDM设计更改后,运行它(并确保EDMX文件已签出和编辑!)
我认为这是另一个功能可能需要在更高版本的来看待EF。具有名为Address1,Address2等导航属性是没有用的。
约拉EDMX文件到内存中,并分析它的基本灵感来自这里:http://www.codeproject.com/KB/library/EdmxParsing.aspx
在VB代码长疙瘩和位启动,但你在这里:
<#@ template language="VB" debug="false" hostspecific="true"#> <#@ import namespace="<xmlns=\"http://schemas.microsoft.com/ado/2008/09/edm\">" #> <#@ import namespace="<xmlns:edmx=\"http://schemas.microsoft.com/ado/2008/10/edmx\">" #> <#@ import namespace="System.Xml" #> <#@ import namespace="System.Xml.Linq" #> 'EDMX pre wash template 'Last run:<#= GetDate() #> <# Main() #> <#+ '---------------------------------------------------------------------------------------------------------- ' Main '---------------------------------------------------------------------------------------------------------- ''' ''' Parses the EDMX file and renames all navigation properties which are not collections and do not ''' reference types by primary key with a their FK name, e.g. navigation property for DefaultAddress_FK is ''' renamed to DefaultAddress ''' Public Sub Main() Dim strPath As String = System.IO.Path.GetDirectoryName(Host.TemplateFile) & "\MyDataModel.edmx" Dim edmx As XElement = XElement.Load(strPath) Dim itemCol As EdmItemCollection = ReadEdmItemCollection(edmx) Dim entity As EntityType Dim entityTo As EntityType Dim navigationProperties As IEnumerable(Of NavigationProperty) Dim navigationProperty As NavigationProperty Dim updatableProperty As XElement Dim assType As AssociationType Dim rc As ReferentialConstraint Dim strPropertyName As String Dim bModifyProperty As Boolean = False For Each entity In itemCol.GetItems(Of EntityType)().OrderBy(Function(e) e.Name) navigationProperties = From n In entity.NavigationProperties Where n.DeclaringType Is entity AndAlso n.ToEndMember.RelationshipMultiplicity RelationshipMultiplicity.Many If navigationProperties.Any() Then For Each navigationProperty In navigationProperties bModifyProperty = False ' Get the association for this navigation property assType = (From ass As AssociationType In itemCol.GetItems(Of AssociationType)() _ Where ass.AssociationEndMembers IsNot Nothing _ AndAlso ass.Name = navigationProperty.RelationshipType.Name _ Select ass).AsQueryable().FirstOrDefault() If (assType IsNot Nothing) Then rc = assType.ReferentialConstraints.FirstOrDefault() If (rc IsNot Nothing AndAlso rc.ToProperties.Any) Then strPropertyName = rc.ToProperties.First.Name ' Make sure the FK is not also a PK on the entity referenced entityTo = (From e In itemCol.GetItems(Of EntityType)() Where e.Name = rc.ToRole.Name).FirstOrDefault() If (entityTo IsNot Nothing AndAlso Not (From km In entityTo.KeyMembers() Where km.Name = strPropertyName).Any) Then ' Get the new name of the property - this uses a little extension ' method I wrote to Trim characters at the end of a string matching a regex strPropertyName = strPropertyName.TrimEnd("_FK[0-9]{0,1}", options:=0) ' Ensure there are no already existant properties with that name on the entity If (Not (From p In entity.Properties Where p IsNot navigationProperty AndAlso p.Name = strPropertyName).Any) Then bModifyProperty = True End If End If If (bModifyProperty) Then updatableProperty = (From n In (From e In edmx... Where [email protected] = entity.Name). Where [email protected] = navigationProperty.Name).FirstOrDefault If (updatableProperty IsNot Nothing AndAlso [email protected] strPropertyName) Then #>'Renaming navigation property on <#= entity.Name #> from <#= [email protected] #> to <#= strPropertyName #> in EDMX file <#+ [email protected] = strPropertyName End If End If End If End If Next End If Next entity edmx.Save(strPath) End Sub '---------------------------------------------------------------------------------------------------------- ' ReadEdmItemCollection '---------------------------------------------------------------------------------------------------------- ''' ''' Code to parse the EDMX xml document and return the managed EdmItemCollection class ''' ''' Taken from here: http://www.codeproject.com/KB/library/EdmxParsing.aspx Public Shared Function ReadEdmItemCollection(edmx As XElement) As EdmItemCollection Dim csdlNodes As IEnumerable(Of XElement) = edmx....First.Elements Dim readers As IEnumerable(Of XMLReader) = From c As XElement In csdlNodes Select c.CreateReader() Return New EdmItemCollection(readers) End Function #>
答
感谢James Close,这确实有效。
这是改写EDMX导航&简单的属性,然后修复映射和协会C#T4模板(它看起来像詹姆斯VB模板):
<#@ template debug="true" hostSpecific="true" #>
<#@ assembly name="System.Text.RegularExpressions"#>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#/*CodeGenerationCommon.ttinclude contains TypeMapper and EdmMetadataLoader from Model.tt, moved it from there to avoid duplication*/#>
<#@ include file="CodeGenerationCommon.ttinclude" #>
<#@ output extension=".txt" #>
Edmx fixer template
Started at: <#= DateTime.Now #>
<#
const string inputFile = @"Model.edmx";
var textTransform = DynamicTextTransformation.Create(this);
var edmx = XElement.Load(textTransform.Host.ResolvePath(inputFile), LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
var code = new CodeGenerationTools(this);
var ef = new MetadataTools(this);
var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
var navigationProperties = typeMapper.GetItemsToGenerate<EntityType>(itemCollection).SelectMany(item => typeMapper.GetNavigationProperties(item));
Fix(navigationProperties, edmx);
edmx.Save(textTransform.Host.ResolvePath(inputFile));
#>
Finished at: <#= DateTime.Now #>
<#+
public void Fix(IEnumerable<NavigationProperty> navigationProperties, XElement edmx)
{
foreach(var navigationProperty in navigationProperties)
{
if((navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many && navigationProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) ||
(navigationProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many && navigationProperty.FromEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many))
{
continue;
}
var fk = navigationProperty.GetDependentProperties().FirstOrDefault();
if(fk == null)
{
var mirrorFk = navigationProperties.FirstOrDefault(item => !item.Equals(navigationProperty) && item.RelationshipType.Name == navigationProperty.RelationshipType.Name).GetDependentProperties().First();
RewriteNavigationProperty(navigationProperty, mirrorFk.Name, edmx, true);
continue;
}
RewriteNavigationProperty(navigationProperty, fk.Name, edmx, false);
}
}
public void RewriteNavigationProperty(NavigationProperty navigationProperty, string fkName, XElement edmx, bool isCollection)
{
var entity = edmx
.Descendants()
.Where(item => item.Name.LocalName == "ConceptualModels")
.Descendants()
.First(item => item.Name.LocalName == "EntityType" && item.Attribute("Name").Value == navigationProperty.DeclaringType.Name);
var element = entity
.Elements()
.First(item => item.Name.LocalName == "NavigationProperty" && item.Attribute("Relationship").Value == navigationProperty.RelationshipType.ToString());
var trimId = new Regex(@"(.*)(ID|Id|id)$").Match(fkName).Groups[1].Value;
var trimDigits = new Regex(@"(.*)(\d*)$").Match(navigationProperty.Name).Groups[1].Value;
var suffix = string.IsNullOrEmpty(trimDigits) ? navigationProperty.Name : trimDigits;
var prefix = string.IsNullOrEmpty(trimId) ? fkName : trimId;
if(string.IsNullOrEmpty(trimId) && !isCollection)
{
FixFk(edmx, entity, fkName, navigationProperty);
}
element.SetAttributeValue("Name", isCollection ? prefix + suffix : prefix);
}
public void FixFk(XElement edmx, XElement entity, string fkName, NavigationProperty navigationProperty)
{
var newFkName = fkName + "Id";
var fk = entity
.Elements()
.First(item => item.Name.LocalName == "Property" && item.Attribute("Name").Value == fkName);
fk.SetAttributeValue("Name", newFkName);
var association = edmx
.Descendants()
.Where(item => item.Name.LocalName == "ConceptualModels")
.Descendants()
.FirstOrDefault(item => item.Name.LocalName == "Association" && item.Attribute("Name").Value == navigationProperty.RelationshipType.Name)
.Descendants()
.FirstOrDefault(item => item.Name.LocalName == "Dependent" && item.Attribute("Role").Value == navigationProperty.DeclaringType.Name)
.Elements()
.First(item => item.Name.LocalName == "PropertyRef");
association.SetAttributeValue("Name", newFkName);
var mapping = edmx
.Descendants()
.Where(item => item.Name.LocalName == "Mappings")
.Descendants()
.FirstOrDefault(item => item.Name.LocalName == "EntityTypeMapping" && item.Attribute("TypeName").Value == navigationProperty.DeclaringType.FullName)
.Descendants()
.First(item => item.Name.LocalName == "ScalarProperty" && item.Attribute("Name").Value == fkName);
mapping.SetAttributeValue("Name", newFkName);
}
#>
嗨詹姆斯,到目前为止,我还没有找到答案。鉴于需要在其他领域推进该项目,我基本放弃了这一功能。谢谢你的建议答案! 1+提供答案。我要验证我的解决方案,如果它能够工作或使我更接近我,我会接受它作为“答案”。再次感谢! – Randy 2012-07-20 12:25:21
对于尝试使用此代码的人,请在顶部添加''来解析一堆引用。并且记住,可能有些字符已经从粘贴代码中删除(可能是SO显示代码的方式)。例如'edmx .... First.Elements'必须是'edmx ... .First.Elements'(afaik)。谢谢@JamesClose –
2015-09-18 11:31:31