Mapping.tt 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <#
  2. // Simplifying assumptions based on reverse engineer rules
  3. // - No complex types
  4. // - One entity container
  5. // - No inheritance
  6. // - Always have two navigation properties
  7. // - All associations expose FKs (except many:many)
  8. #>
  9. <#@ template hostspecific="true" language="C#" #>
  10. <#@ include file="EF.Utility.CS.ttinclude" #><#@
  11. output extension=".cs" #><#
  12. var efHost = (EfTextTemplateHost)Host;
  13. var code = new CodeGenerationTools(this);
  14. if (efHost.EntityFrameworkVersion >= new Version(4, 4))
  15. {
  16. #>
  17. using System.ComponentModel.DataAnnotations.Schema;
  18. <#
  19. }
  20. else
  21. {
  22. #>
  23. using System.ComponentModel.DataAnnotations;
  24. <#
  25. }
  26. #>
  27. using System.Data.Entity.ModelConfiguration;
  28. namespace <#= code.EscapeNamespace(efHost.Namespace) #>
  29. {
  30. public class <#= efHost.EntityType.Name #>Map : EntityTypeConfiguration<<#= efHost.EntityType.Name #>>
  31. {
  32. public <#= efHost.EntityType.Name #>Map()
  33. {
  34. // Primary Key
  35. <#
  36. if (efHost.EntityType.KeyMembers.Count() == 1)
  37. {
  38. #>
  39. this.HasKey(t => t.<#= efHost.EntityType.KeyMembers.Single().Name #>);
  40. <#
  41. }
  42. else
  43. {
  44. #>
  45. this.HasKey(t => new { <#= string.Join(", ", efHost.EntityType.KeyMembers.Select(m => "t." + m.Name)) #> });
  46. <#
  47. }
  48. #>
  49. // Properties
  50. <#
  51. foreach (var prop in efHost.EntityType.Properties)
  52. {
  53. var type = (PrimitiveType)prop.TypeUsage.EdmType;
  54. var isKey = efHost.EntityType.KeyMembers.Contains(prop);
  55. var storeProp = efHost.PropertyToColumnMappings[prop];
  56. var sgpFacet = storeProp.TypeUsage.Facets.SingleOrDefault(f => f.Name == "StoreGeneratedPattern");
  57. var storeGeneratedPattern = sgpFacet == null
  58. ? StoreGeneratedPattern.None
  59. : (StoreGeneratedPattern)sgpFacet.Value;
  60. var configLines = new List<string>();
  61. if (type.ClrEquivalentType == typeof(int)
  62. || type.ClrEquivalentType == typeof(decimal)
  63. || type.ClrEquivalentType == typeof(short)
  64. || type.ClrEquivalentType == typeof(long))
  65. {
  66. if (isKey && storeGeneratedPattern != StoreGeneratedPattern.Identity)
  67. {
  68. configLines.Add(".HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)");
  69. }
  70. else if ((!isKey || efHost.EntityType.KeyMembers.Count > 1) && storeGeneratedPattern == StoreGeneratedPattern.Identity)
  71. {
  72. configLines.Add(".HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)");
  73. }
  74. }
  75. if (type.ClrEquivalentType == typeof(string)
  76. || type.ClrEquivalentType == typeof(byte[]))
  77. {
  78. if (!prop.Nullable)
  79. {
  80. configLines.Add(".IsRequired()");
  81. }
  82. var unicodeFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "IsUnicode");
  83. if(unicodeFacet != null && !(bool)unicodeFacet.Value)
  84. {
  85. configLines.Add(".IsUnicode(false)");
  86. }
  87. var fixedLengthFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "FixedLength");
  88. if (fixedLengthFacet != null && (bool)fixedLengthFacet.Value)
  89. {
  90. configLines.Add(".IsFixedLength()");
  91. }
  92. var maxLengthFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "MaxLength");
  93. if (maxLengthFacet != null && !maxLengthFacet.IsUnbounded)
  94. {
  95. configLines.Add(string.Format(".HasMaxLength({0})", maxLengthFacet.Value));
  96. if (storeGeneratedPattern == StoreGeneratedPattern.Computed
  97. && type.ClrEquivalentType == typeof(byte[])
  98. && (int)maxLengthFacet.Value == 8)
  99. {
  100. configLines.Add(".IsRowVersion()");
  101. }
  102. }
  103. }
  104. if(configLines.Any())
  105. {
  106. #>
  107. this.Property(t => t.<#= prop.Name #>)
  108. <#= string.Join("\r\n ", configLines) #>;
  109. <#
  110. }
  111. }
  112. var tableSet = efHost.TableSet;
  113. var tableName = (string)tableSet.MetadataProperties["Table"].Value
  114. ?? tableSet.Name;
  115. var schemaName = (string)tableSet.MetadataProperties["Schema"].Value;
  116. #>
  117. // Table & Column Mappings
  118. <#
  119. if (schemaName == "dbo" || string.IsNullOrWhiteSpace(schemaName))
  120. {
  121. #>
  122. this.ToTable("<#= tableName #>");
  123. <#
  124. }
  125. else
  126. {
  127. #>
  128. this.ToTable("<#= tableName #>", "<#= schemaName #>");
  129. <#
  130. }
  131. foreach (var property in efHost.EntityType.Properties)
  132. {
  133. #>
  134. this.Property(t => t.<#= property.Name #>).HasColumnName("<#= efHost.PropertyToColumnMappings[property].Name #>");
  135. <#
  136. }
  137. // Find m:m relationshipsto configure
  138. var manyManyRelationships = efHost.EntityType.NavigationProperties
  139. .Where(np => np.DeclaringType == efHost.EntityType
  140. && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many
  141. && np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many
  142. && np.RelationshipType.RelationshipEndMembers.First() == np.FromEndMember); // <- ensures we only configure from one end
  143. // Find FK relationships that this entity is the dependent of
  144. var fkRelationships = efHost.EntityType.NavigationProperties
  145. .Where(np => np.DeclaringType == efHost.EntityType
  146. && ((AssociationType)np.RelationshipType).IsForeignKey
  147. && ((AssociationType)np.RelationshipType).ReferentialConstraints.Single().ToRole == np.FromEndMember);
  148. if(manyManyRelationships.Any() || fkRelationships.Any())
  149. {
  150. #>
  151. // Relationships
  152. <#
  153. foreach (var navProperty in manyManyRelationships)
  154. {
  155. var otherNavProperty = navProperty.ToEndMember.GetEntityType().NavigationProperties.Where(n => n.RelationshipType == navProperty.RelationshipType && n != navProperty).Single();
  156. var association = (AssociationType)navProperty.RelationshipType;
  157. var mapping = efHost.ManyToManyMappings[association];
  158. var item1 = mapping.Item1;
  159. var mappingTableName = (string)mapping.Item1.MetadataProperties["Table"].Value
  160. ?? item1.Name;
  161. var mappingSchemaName = (string)item1.MetadataProperties["Schema"].Value;
  162. // Need to ensure that FKs are decalred in the same order as the PK properties on each principal type
  163. var leftType = (EntityType)navProperty.DeclaringType;
  164. var leftKeyMappings = mapping.Item2[navProperty.FromEndMember];
  165. var leftColumns = string.Join(", ", leftType.KeyMembers.Select(m => "\"" + leftKeyMappings[m] + "\""));
  166. var rightType = (EntityType)otherNavProperty.DeclaringType;
  167. var rightKeyMappings = mapping.Item2[otherNavProperty.FromEndMember];
  168. var rightColumns = string.Join(", ", rightType.KeyMembers.Select(m => "\"" + rightKeyMappings[m] + "\""));
  169. #>
  170. this.HasMany(t => t.<#= code.Escape(navProperty) #>)
  171. .WithMany(t => t.<#= code.Escape(otherNavProperty) #>)
  172. .Map(m =>
  173. {
  174. <#
  175. if (mappingSchemaName == "dbo" || string.IsNullOrWhiteSpace(mappingSchemaName))
  176. {
  177. #>
  178. m.ToTable("<#= mappingTableName #>");
  179. <#
  180. }
  181. else
  182. {
  183. #>
  184. m.ToTable("<#= mappingTableName #>", "<#= mappingSchemaName #>");
  185. <#
  186. }
  187. #>
  188. m.MapLeftKey(<#= leftColumns #>);
  189. m.MapRightKey(<#= rightColumns #>);
  190. });
  191. <#
  192. }
  193. foreach (var navProperty in fkRelationships)
  194. {
  195. var otherNavProperty = navProperty.ToEndMember.GetEntityType().NavigationProperties.Where(n => n.RelationshipType == navProperty.RelationshipType && n != navProperty).Single();
  196. var association = (AssociationType)navProperty.RelationshipType;
  197. if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
  198. {
  199. #>
  200. this.HasRequired(t => t.<#= code.Escape(navProperty) #>)
  201. <#
  202. }
  203. else
  204. {
  205. #>
  206. this.HasOptional(t => t.<#= code.Escape(navProperty) #>)
  207. <#
  208. }
  209. if(navProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
  210. {
  211. #>
  212. .WithMany(t => t.<#= code.Escape(otherNavProperty) #>)
  213. <#
  214. if(association.ReferentialConstraints.Single().ToProperties.Count == 1)
  215. {
  216. #>
  217. .HasForeignKey(d => d.<#= association.ReferentialConstraints.Single().ToProperties.Single().Name #>);
  218. <#
  219. }
  220. else
  221. {
  222. #>
  223. .HasForeignKey(d => new { <#= string.Join(", ", association.ReferentialConstraints.Single().ToProperties.Select(p => "d." + p.Name)) #> });
  224. <#
  225. }
  226. }
  227. else
  228. {
  229. // NOTE: We can assume that this is a required:optional relationship
  230. // as EDMGen will never create an optional:optional relationship
  231. // because everything is one:many except PK-PK relationships which must be required
  232. #>
  233. .WithOptional(t => t.<#= code.Escape(otherNavProperty) #>);
  234. <#
  235. }
  236. }
  237. #>
  238. <#
  239. }
  240. #>
  241. }
  242. }
  243. }