1 //--------------------------------------------------------------------- 2 // <copyright file="FunctionImportMapping.ReturnTypeRanameMapping.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System; 11 using System.Collections.Generic; 12 using System.Linq; 13 using System.Text; 14 using System.Data.Metadata.Edm; 15 using System.Data.Common.Utils; 16 using System.Xml; 17 using System.Collections.ObjectModel; 18 using System.Diagnostics; 19 20 namespace System.Data.Mapping 21 { 22 internal abstract class FunctionImportStructuralTypeMapping 23 { 24 internal readonly LineInfo LineInfo; 25 internal readonly Collection<FunctionImportReturnTypePropertyMapping> ColumnsRenameList; 26 FunctionImportStructuralTypeMapping(Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)27 internal FunctionImportStructuralTypeMapping(Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo) 28 { 29 this.ColumnsRenameList = columnsRenameList; 30 this.LineInfo = lineInfo; 31 } 32 } 33 34 internal sealed class FunctionImportEntityTypeMapping : FunctionImportStructuralTypeMapping 35 { FunctionImportEntityTypeMapping(IEnumerable<EntityType> isOfTypeEntityTypes, IEnumerable<EntityType> entityTypes, IEnumerable<FunctionImportEntityTypeMappingCondition> conditions, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)36 internal FunctionImportEntityTypeMapping(IEnumerable<EntityType> isOfTypeEntityTypes, 37 IEnumerable<EntityType> entityTypes, IEnumerable<FunctionImportEntityTypeMappingCondition> conditions, 38 Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, 39 LineInfo lineInfo) 40 : base(columnsRenameList, lineInfo) 41 { 42 this.IsOfTypeEntityTypes = new ReadOnlyCollection<EntityType>( 43 EntityUtil.CheckArgumentNull(isOfTypeEntityTypes, "isOfTypeEntityTypes").ToList()); 44 this.EntityTypes = new ReadOnlyCollection<EntityType>( 45 EntityUtil.CheckArgumentNull(entityTypes, "entityTypes").ToList()); 46 this.Conditions = new ReadOnlyCollection<FunctionImportEntityTypeMappingCondition>( 47 EntityUtil.CheckArgumentNull(conditions, "conditions").ToList()); 48 } 49 50 internal readonly ReadOnlyCollection<FunctionImportEntityTypeMappingCondition> Conditions; 51 internal readonly ReadOnlyCollection<EntityType> EntityTypes; 52 internal readonly ReadOnlyCollection<EntityType> IsOfTypeEntityTypes; 53 54 /// <summary> 55 /// Gets all (concrete) entity types implied by this type mapping. 56 /// </summary> GetMappedEntityTypes(ItemCollection itemCollection)57 internal IEnumerable<EntityType> GetMappedEntityTypes(ItemCollection itemCollection) 58 { 59 const bool includeAbstractTypes = false; 60 return this.EntityTypes.Concat( 61 this.IsOfTypeEntityTypes.SelectMany(entityType => 62 MetadataHelper.GetTypeAndSubtypesOf(entityType, itemCollection, includeAbstractTypes) 63 .Cast<EntityType>())); 64 } 65 GetDiscriminatorColumns()66 internal IEnumerable<String> GetDiscriminatorColumns() 67 { 68 return this.Conditions.Select(condition => condition.ColumnName); 69 } 70 } 71 72 internal sealed class FunctionImportComplexTypeMapping : FunctionImportStructuralTypeMapping 73 { 74 internal readonly ComplexType ReturnType; 75 FunctionImportComplexTypeMapping(ComplexType returnType, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)76 internal FunctionImportComplexTypeMapping(ComplexType returnType, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo) 77 : base(columnsRenameList, lineInfo) 78 { 79 this.ReturnType = returnType; 80 } 81 } 82 83 internal abstract class FunctionImportReturnTypePropertyMapping 84 { 85 internal readonly string CMember; 86 internal readonly string SColumn; 87 internal readonly LineInfo LineInfo; 88 FunctionImportReturnTypePropertyMapping(string cMember, string sColumn, LineInfo lineInfo)89 internal FunctionImportReturnTypePropertyMapping(string cMember, string sColumn, LineInfo lineInfo) 90 { 91 this.CMember = cMember; 92 this.SColumn = sColumn; 93 this.LineInfo = lineInfo; 94 } 95 } 96 97 internal sealed class FunctionImportReturnTypeScalarPropertyMapping : FunctionImportReturnTypePropertyMapping 98 { FunctionImportReturnTypeScalarPropertyMapping(string cMember, string sColumn, LineInfo lineInfo)99 internal FunctionImportReturnTypeScalarPropertyMapping(string cMember, string sColumn, LineInfo lineInfo) 100 : base(cMember, sColumn, lineInfo) 101 { 102 } 103 } 104 105 /// <summary> 106 /// extract the column rename info from polymorphic entity type mappings 107 /// </summary> 108 internal sealed class FunctionImportReturnTypeEntityTypeColumnsRenameBuilder 109 { 110 /// <summary> 111 /// CMember -> SMember* 112 /// </summary> 113 internal Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping> ColumnRenameMapping; 114 FunctionImportReturnTypeEntityTypeColumnsRenameBuilder( Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> isOfTypeEntityTypeColumnsRenameMapping, Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> entityTypeColumnsRenameMapping)115 internal FunctionImportReturnTypeEntityTypeColumnsRenameBuilder( 116 Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> isOfTypeEntityTypeColumnsRenameMapping, 117 Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> entityTypeColumnsRenameMapping) 118 { 119 EntityUtil.CheckArgumentNull(isOfTypeEntityTypeColumnsRenameMapping, "isOfTypeEntityTypeColumnsRenameMapping"); 120 EntityUtil.CheckArgumentNull(entityTypeColumnsRenameMapping, "entityTypeColumnsRenameMapping"); 121 122 this.ColumnRenameMapping = new Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping>(); 123 124 // Assign the columns renameMapping to the result dictionary. 125 foreach (EntityType entityType in isOfTypeEntityTypeColumnsRenameMapping.Keys) 126 { 127 this.SetStructuralTypeColumnsRename( 128 entityType, isOfTypeEntityTypeColumnsRenameMapping[entityType], true/*isTypeOf*/); 129 } 130 131 foreach (EntityType entityType in entityTypeColumnsRenameMapping.Keys) 132 { 133 this.SetStructuralTypeColumnsRename( 134 entityType, entityTypeColumnsRenameMapping[entityType], false/*isTypeOf*/); 135 } 136 } 137 138 /// <summary> 139 /// Set the column mappings for each defaultMemberName. 140 /// </summary> SetStructuralTypeColumnsRename( EntityType entityType, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameMapping, bool isTypeOf)141 private void SetStructuralTypeColumnsRename( 142 EntityType entityType, 143 Collection<FunctionImportReturnTypePropertyMapping> columnsRenameMapping, 144 bool isTypeOf) 145 { 146 EntityUtil.CheckArgumentNull(entityType, "entityType"); 147 EntityUtil.CheckArgumentNull(columnsRenameMapping, "columnsRenameMapping"); 148 149 foreach (var mapping in columnsRenameMapping) 150 { 151 if (!this.ColumnRenameMapping.Keys.Contains(mapping.CMember)) 152 { 153 this.ColumnRenameMapping[mapping.CMember] = new FunctionImportReturnTypeStructuralTypeColumnRenameMapping(mapping.CMember); 154 } 155 this.ColumnRenameMapping[mapping.CMember].AddRename(new FunctionImportReturnTypeStructuralTypeColumn(mapping.SColumn, entityType, isTypeOf, mapping.LineInfo)); 156 } 157 } 158 } 159 160 internal sealed class FunctionImportReturnTypeStructuralTypeColumn 161 { 162 internal readonly StructuralType Type; 163 internal readonly bool IsTypeOf; 164 internal readonly string ColumnName; 165 internal readonly LineInfo LineInfo; 166 FunctionImportReturnTypeStructuralTypeColumn(string columnName, StructuralType type, bool isTypeOf, LineInfo lineInfo)167 internal FunctionImportReturnTypeStructuralTypeColumn(string columnName, StructuralType type, bool isTypeOf, LineInfo lineInfo) 168 { 169 this.ColumnName = columnName; 170 this.IsTypeOf = isTypeOf; 171 this.Type = type; 172 this.LineInfo = lineInfo; 173 } 174 } 175 176 internal class FunctionImportReturnTypeStructuralTypeColumnRenameMapping 177 { 178 private Collection<FunctionImportReturnTypeStructuralTypeColumn> _columnListForType; 179 private Collection<FunctionImportReturnTypeStructuralTypeColumn> _columnListForIsTypeOfType; 180 /// <summary> 181 /// Null if default mapping is not allowed. 182 /// </summary> 183 private readonly string _defaultMemberName; 184 private Memoizer<StructuralType, FunctionImportReturnTypeStructuralTypeColumn> _renameCache; 185 FunctionImportReturnTypeStructuralTypeColumnRenameMapping(string defaultMemberName)186 internal FunctionImportReturnTypeStructuralTypeColumnRenameMapping(string defaultMemberName) 187 { 188 this._defaultMemberName = defaultMemberName; 189 this._columnListForType = new Collection<FunctionImportReturnTypeStructuralTypeColumn>(); 190 this._columnListForIsTypeOfType = new Collection<FunctionImportReturnTypeStructuralTypeColumn>(); 191 this._renameCache = new Memoizer<StructuralType, FunctionImportReturnTypeStructuralTypeColumn>( 192 this.GetRename, EqualityComparer<StructuralType>.Default); 193 } 194 195 /// <summary> 196 /// <see cref="GetRename(EdmType, out IXmlLineInfo)"/> for more info. 197 /// </summary> GetRename(EdmType type)198 internal string GetRename(EdmType type) 199 { 200 IXmlLineInfo lineInfo; 201 return GetRename(type, out lineInfo); 202 } 203 204 /// <summary> 205 /// A default mapping (property "Foo" maps by convention to column "Foo"), if allowed, has the lowest precedence. 206 /// A mapping for a specific type (EntityType="Bar") takes precedence over a mapping for a hierarchy (EntityType="IsTypeOf(Bar)")) 207 /// If there are two hierarchy mappings, the most specific mapping takes precedence. 208 /// For instance, given the types Base, Derived1 : Base, and Derived2 : Derived1, 209 /// w.r.t. Derived1 "IsTypeOf(Derived1)" takes precedence over "IsTypeOf(Base)" when you ask for the rename of Derived1 210 /// </summary> 211 /// <param name="lineInfo">Empty for default rename mapping.</param> GetRename(EdmType type, out IXmlLineInfo lineInfo)212 internal string GetRename(EdmType type, out IXmlLineInfo lineInfo) 213 { 214 Debug.Assert(type is StructuralType, "we can only rename structural type"); 215 EntityUtil.CheckArgumentNull(type, "type"); 216 217 var rename = this._renameCache.Evaluate(type as StructuralType); 218 lineInfo = rename.LineInfo; 219 return rename.ColumnName; 220 } 221 GetRename(StructuralType typeForRename)222 private FunctionImportReturnTypeStructuralTypeColumn GetRename(StructuralType typeForRename) 223 { 224 FunctionImportReturnTypeStructuralTypeColumn ofTypecolumn = _columnListForType.FirstOrDefault(t => t.Type == typeForRename); 225 if (null != ofTypecolumn) 226 { 227 return ofTypecolumn; 228 } 229 230 // if there are duplicate istypeof mapping defined rename for the same column, the last one wins 231 FunctionImportReturnTypeStructuralTypeColumn isOfTypeColumn = _columnListForIsTypeOfType.Where(t => t.Type == typeForRename).LastOrDefault(); 232 233 if (null != isOfTypeColumn) 234 { 235 return isOfTypeColumn; 236 } 237 else 238 { 239 // find out all the tyes that is isparent type of this lookup type 240 IEnumerable<FunctionImportReturnTypeStructuralTypeColumn> nodesInBaseHierachy = 241 _columnListForIsTypeOfType.Where(t => t.Type.IsAssignableFrom(typeForRename)); 242 243 if (nodesInBaseHierachy.Count() == 0) 244 { 245 // non of its parent is renamed, so it will take the default one 246 return new FunctionImportReturnTypeStructuralTypeColumn(this._defaultMemberName, typeForRename, false, null); 247 } 248 else 249 { 250 // we will guarantee that there will be some mapping for us on this column 251 // find out which one is lowest on the link 252 return GetLowestParentInHierachy(nodesInBaseHierachy); 253 } 254 } 255 } 256 GetLowestParentInHierachy(IEnumerable<FunctionImportReturnTypeStructuralTypeColumn> nodesInHierachy)257 private FunctionImportReturnTypeStructuralTypeColumn GetLowestParentInHierachy(IEnumerable<FunctionImportReturnTypeStructuralTypeColumn> nodesInHierachy) 258 { 259 FunctionImportReturnTypeStructuralTypeColumn lowestParent = null; 260 foreach (var node in nodesInHierachy) 261 { 262 if (lowestParent == null) 263 { 264 lowestParent = node; 265 } 266 else if (lowestParent.Type.IsAssignableFrom(node.Type)) 267 { 268 lowestParent = node; 269 } 270 } 271 Debug.Assert(null != lowestParent, "We should have the lowest parent"); 272 return lowestParent; 273 } 274 AddRename(FunctionImportReturnTypeStructuralTypeColumn renamedColumn)275 internal void AddRename(FunctionImportReturnTypeStructuralTypeColumn renamedColumn) 276 { 277 EntityUtil.CheckArgumentNull(renamedColumn, "renamedColumn"); 278 279 if (!renamedColumn.IsTypeOf) 280 { 281 // add to collection if the mapping is for specific type 282 this._columnListForType.Add(renamedColumn); 283 } 284 else 285 { 286 _columnListForIsTypeOfType.Add(renamedColumn); 287 } 288 } 289 } 290 } 291