Skip to content

Instantly share code, notes, and snippets.

@mikecole
Created November 15, 2013 22:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikecole/7493050 to your computer and use it in GitHub Desktop.
Save mikecole/7493050 to your computer and use it in GitHub Desktop.
Reverse Engineer Code First Mapping template
<#
// 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