<# // 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; using OpenAuth.Domain; 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(); 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) #>); <# } } #> <# } #> } } }