使用EF4(edmx)模型时,我们经常需要处理“从数据库更新模型”。通常,我们需要删除表,并让它们从数据库中完全重新生成。
手头的问题是我们有多个递归关系/属性。默认情况下,“从数据库更新模型”过程使用对象的名称创建属性,然后为每个附加关系添加1,2,3等。因此,如果我有一个“公司”表,它多次指向自己(如母公司和dba公司),目前edmx的结果是Company1和Company2。我需要控制它们的命名....而不是手动。
如果我能找到T4文件(或拦截和控制的方法)生成edmx文件本身,我可以解决这个问题。
答案 0 :(得分:4)
在寻找其他东西时偶然发现了这个问题,所以我希望你自己解决了这个问题。不久之后,我遇到了与你完全相同的问题。我绕过它的方法是使用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 e.@Name = entity.Name). Where n.@Name = navigationProperty.Name).FirstOrDefault If (updatableProperty IsNot Nothing AndAlso updatableProperty.@Name strPropertyName) Then #>'Renaming navigation property on <#= entity.Name #> from <#= updatableProperty.@Name #> to <#= strPropertyName #> in EDMX file <#+ updatableProperty.@Name = 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 #>
答案 1 :(得分:2)
感谢James Close,这确实有用。
这是重写edmx导航和放大器的C#T4模板(看起来像James 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);
}
#>