什么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 
#> 

+0

嗨詹姆斯,到目前为止,我还没有找到答案。鉴于需要在其他领域推进该项目,我基本放弃了这一功能。谢谢你的建议答案! 1+提供答案。我要验证我的解决方案,如果它能够工作或使我更接近我,我会接受它作为“答案”。再次感谢! – Randy 2012-07-20 12:25:21

+0

对于尝试使用此代码的人,请在顶部添加''来解析一堆引用。并且记住,可能有些字符已经从粘贴代码中删除(可能是SO显示代码的方式)。例如'edmx .... First.Elements'必须是'edmx ... .First.Elements'(afaik)。谢谢@JamesClose – 2015-09-18 11:31:31

感谢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); 
    } 
#>