Created
November 15, 2013 22:50
-
-
Save mikecole/7493050 to your computer and use it in GitHub Desktop.
Reverse Engineer Code First Mapping template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
// Simplifying assumptions based on reverse engineer rules | |
// - No complex types | |
// - One entity container | |
// - No inheritance | |
// - Always have two navigation properties | |
// - All associations expose FKs (except many:many) | |
#> | |
<#@ template hostspecific="true" language="C#" #> | |
<#@ include file="EF.Utility.CS.ttinclude" #><#@ | |
output extension=".cs" #><# | |
var efHost = (EfTextTemplateHost)Host; | |
var code = new CodeGenerationTools(this); | |
if (efHost.EntityFrameworkVersion >= new Version(4, 4)) | |
{ | |
#> | |
using System.ComponentModel.DataAnnotations.Schema; | |
<# | |
} | |
else | |
{ | |
#> | |
using System.ComponentModel.DataAnnotations; | |
<# | |
} | |
#> | |
using System.Data.Entity.ModelConfiguration; | |
namespace <#= code.EscapeNamespace(efHost.Namespace) #> | |
{ | |
public class <#= efHost.EntityType.Name #>Map : EntityTypeConfiguration<<#= efHost.EntityType.Name #>> | |
{ | |
public <#= efHost.EntityType.Name #>Map() | |
{ | |
// Primary Key | |
<# | |
if (efHost.EntityType.KeyMembers.Count() == 1) | |
{ | |
#> | |
this.HasKey(t => t.<#= efHost.EntityType.KeyMembers.Single().Name #>); | |
<# | |
} | |
else | |
{ | |
#> | |
this.HasKey(t => new { <#= string.Join(", ", efHost.EntityType.KeyMembers.Select(m => "t." + m.Name)) #> }); | |
<# | |
} | |
#> | |
// Properties | |
<# | |
foreach (var prop in efHost.EntityType.Properties) | |
{ | |
var type = (PrimitiveType)prop.TypeUsage.EdmType; | |
var isKey = efHost.EntityType.KeyMembers.Contains(prop); | |
var storeProp = efHost.PropertyToColumnMappings[prop]; | |
var sgpFacet = storeProp.TypeUsage.Facets.SingleOrDefault(f => f.Name == "StoreGeneratedPattern"); | |
var storeGeneratedPattern = sgpFacet == null | |
? StoreGeneratedPattern.None | |
: (StoreGeneratedPattern)sgpFacet.Value; | |
var configLines = new List<string>(); | |
if (type.ClrEquivalentType == typeof(int) | |
|| type.ClrEquivalentType == typeof(decimal) | |
|| type.ClrEquivalentType == typeof(short) | |
|| type.ClrEquivalentType == typeof(long)) | |
{ | |
if (isKey && storeGeneratedPattern != StoreGeneratedPattern.Identity) | |
{ | |
configLines.Add(".HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)"); | |
} | |
else if ((!isKey || efHost.EntityType.KeyMembers.Count > 1) && storeGeneratedPattern == StoreGeneratedPattern.Identity) | |
{ | |
configLines.Add(".HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)"); | |
} | |
} | |
if (type.ClrEquivalentType == typeof(string) | |
|| type.ClrEquivalentType == typeof(byte[])) | |
{ | |
if (!prop.Nullable) | |
{ | |
configLines.Add(".IsRequired()"); | |
} | |
var unicodeFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "IsUnicode"); | |
if(unicodeFacet != null && !(bool)unicodeFacet.Value) | |
{ | |
configLines.Add(".IsUnicode(false)"); | |
} | |
var fixedLengthFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "FixedLength"); | |
if (fixedLengthFacet != null && (bool)fixedLengthFacet.Value) | |
{ | |
configLines.Add(".IsFixedLength()"); | |
} | |
var maxLengthFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "MaxLength"); | |
if (maxLengthFacet != null && !maxLengthFacet.IsUnbounded) | |
{ | |
configLines.Add(string.Format(".HasMaxLength({0})", maxLengthFacet.Value)); | |
if (storeGeneratedPattern == StoreGeneratedPattern.Computed | |
&& type.ClrEquivalentType == typeof(byte[]) | |
&& (int)maxLengthFacet.Value == 8) | |
{ | |
configLines.Add(".IsRowVersion()"); | |
} | |
} | |
} | |
if(configLines.Any()) | |
{ | |
#> | |
this.Property(t => t.<#= prop.Name #>) | |
<#= string.Join("\r\n ", configLines) #>; | |
<# | |
} | |
} | |
var tableSet = efHost.TableSet; | |
var tableName = (string)tableSet.MetadataProperties["Table"].Value | |
?? tableSet.Name; | |
var schemaName = (string)tableSet.MetadataProperties["Schema"].Value; | |
#> | |
// Table & Column Mappings | |
<# | |
if (schemaName == "dbo" || string.IsNullOrWhiteSpace(schemaName)) | |
{ | |
#> | |
this.ToTable("<#= tableName #>"); | |
<# | |
} | |
else | |
{ | |
#> | |
this.ToTable("<#= tableName #>", "<#= schemaName #>"); | |
<# | |
} | |
foreach (var property in efHost.EntityType.Properties) | |
{ | |
#> | |
this.Property(t => t.<#= property.Name #>).HasColumnName("<#= efHost.PropertyToColumnMappings[property].Name #>"); | |
<# | |
} | |
// Find m:m relationshipsto configure | |
var manyManyRelationships = efHost.EntityType.NavigationProperties | |
.Where(np => np.DeclaringType == efHost.EntityType | |
&& np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many | |
&& np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many | |
&& np.RelationshipType.RelationshipEndMembers.First() == np.FromEndMember); // <- ensures we only configure from one end | |
// Find FK relationships that this entity is the dependent of | |
var fkRelationships = efHost.EntityType.NavigationProperties | |
.Where(np => np.DeclaringType == efHost.EntityType | |
&& ((AssociationType)np.RelationshipType).IsForeignKey | |
&& ((AssociationType)np.RelationshipType).ReferentialConstraints.Single().ToRole == np.FromEndMember); | |
if(manyManyRelationships.Any() || fkRelationships.Any()) | |
{ | |
#> | |
// Relationships | |
<# | |
foreach (var navProperty in manyManyRelationships) | |
{ | |
var otherNavProperty = navProperty.ToEndMember.GetEntityType().NavigationProperties.Where(n => n.RelationshipType == navProperty.RelationshipType && n != navProperty).Single(); | |
var association = (AssociationType)navProperty.RelationshipType; | |
var mapping = efHost.ManyToManyMappings[association]; | |
var item1 = mapping.Item1; | |
var mappingTableName = (string)mapping.Item1.MetadataProperties["Table"].Value | |
?? item1.Name; | |
var mappingSchemaName = (string)item1.MetadataProperties["Schema"].Value; | |
// Need to ensure that FKs are decalred in the same order as the PK properties on each principal type | |
var leftType = (EntityType)navProperty.DeclaringType; | |
var leftKeyMappings = mapping.Item2[navProperty.FromEndMember]; | |
var leftColumns = string.Join(", ", leftType.KeyMembers.Select(m => "\"" + leftKeyMappings[m] + "\"")); | |
var rightType = (EntityType)otherNavProperty.DeclaringType; | |
var rightKeyMappings = mapping.Item2[otherNavProperty.FromEndMember]; | |
var rightColumns = string.Join(", ", rightType.KeyMembers.Select(m => "\"" + rightKeyMappings[m] + "\"")); | |
#> | |
this.HasMany(t => t.<#= code.Escape(navProperty) #>) | |
.WithMany(t => t.<#= code.Escape(otherNavProperty) #>) | |
.Map(m => | |
{ | |
<# | |
if (mappingSchemaName == "dbo" || string.IsNullOrWhiteSpace(mappingSchemaName)) | |
{ | |
#> | |
m.ToTable("<#= mappingTableName #>"); | |
<# | |
} | |
else | |
{ | |
#> | |
m.ToTable("<#= mappingTableName #>", "<#= mappingSchemaName #>"); | |
<# | |
} | |
#> | |
m.MapLeftKey(<#= leftColumns #>); | |
m.MapRightKey(<#= rightColumns #>); | |
}); | |
<# | |
} | |
foreach (var navProperty in fkRelationships) | |
{ | |
var otherNavProperty = navProperty.ToEndMember.GetEntityType().NavigationProperties.Where(n => n.RelationshipType == navProperty.RelationshipType && n != navProperty).Single(); | |
var association = (AssociationType)navProperty.RelationshipType; | |
if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One) | |
{ | |
#> | |
this.HasRequired(t => t.<#= code.Escape(navProperty) #>) | |
<# | |
} | |
else | |
{ | |
#> | |
this.HasOptional(t => t.<#= code.Escape(navProperty) #>) | |
<# | |
} | |
if(navProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) | |
{ | |
#> | |
.WithMany(t => t.<#= code.Escape(otherNavProperty) #>) | |
<# | |
if(association.ReferentialConstraints.Single().ToProperties.Count == 1) | |
{ | |
#> | |
.HasForeignKey(d => d.<#= association.ReferentialConstraints.Single().ToProperties.Single().Name #>); | |
<# | |
} | |
else | |
{ | |
#> | |
.HasForeignKey(d => new { <#= string.Join(", ", association.ReferentialConstraints.Single().ToProperties.Select(p => "d." + p.Name)) #> }); | |
<# | |
} | |
} | |
else | |
{ | |
// NOTE: We can assume that this is a required:optional relationship | |
// as EDMGen will never create an optional:optional relationship | |
// because everything is one:many except PK-PK relationships which must be required | |
#> | |
.WithOptional(t => t.<#= code.Escape(otherNavProperty) #>); | |
<# | |
} | |
} | |
#> | |
<# | |
} | |
#> | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment