1 //--------------------------------------------------------------------- 2 // <copyright file="StorageModificationFunctionMapping.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 11 using System; 12 using System.Collections.Generic; 13 using System.Collections.ObjectModel; 14 using System.Text; 15 using System.Data.Metadata.Edm; 16 using System.Diagnostics; 17 using System.Globalization; 18 using System.Data.Common.Utils; 19 using System.Linq; 20 21 namespace System.Data.Mapping 22 { 23 /// <summary> 24 /// Describes modification function mappings for an association set. 25 /// </summary> 26 internal sealed class StorageAssociationSetModificationFunctionMapping 27 { StorageAssociationSetModificationFunctionMapping( AssociationSet associationSet, StorageModificationFunctionMapping deleteFunctionMapping, StorageModificationFunctionMapping insertFunctionMapping)28 internal StorageAssociationSetModificationFunctionMapping( 29 AssociationSet associationSet, 30 StorageModificationFunctionMapping deleteFunctionMapping, 31 StorageModificationFunctionMapping insertFunctionMapping) 32 { 33 this.AssociationSet = EntityUtil.CheckArgumentNull(associationSet, "associationSet"); 34 this.DeleteFunctionMapping = deleteFunctionMapping; 35 this.InsertFunctionMapping = insertFunctionMapping; 36 } 37 38 /// <summary> 39 /// Association set these functions handles. 40 /// </summary> 41 internal readonly AssociationSet AssociationSet; 42 43 /// <summary> 44 /// Delete function for this association set. 45 /// </summary> 46 internal readonly StorageModificationFunctionMapping DeleteFunctionMapping; 47 48 /// <summary> 49 /// Insert function for this association set. 50 /// </summary> 51 internal readonly StorageModificationFunctionMapping InsertFunctionMapping; 52 ToString()53 public override string ToString() 54 { 55 return String.Format(CultureInfo.InvariantCulture, 56 "AS{{{0}}}:{3}DFunc={{{1}}},{3}IFunc={{{2}}}", AssociationSet, DeleteFunctionMapping, 57 InsertFunctionMapping, Environment.NewLine + " "); 58 } 59 Print(int index)60 internal void Print(int index) 61 { 62 StorageEntityContainerMapping.GetPrettyPrintString(ref index); 63 StringBuilder sb = new StringBuilder(); 64 sb.Append("Association Set Function Mapping"); 65 sb.Append(" "); 66 sb.Append(this.ToString()); 67 Console.WriteLine(sb.ToString()); 68 } 69 } 70 71 /// <summary> 72 /// Describes modification function mappings for an entity type within an entity set. 73 /// </summary> 74 internal sealed class StorageEntityTypeModificationFunctionMapping 75 { StorageEntityTypeModificationFunctionMapping( EntityType entityType, StorageModificationFunctionMapping deleteFunctionMapping, StorageModificationFunctionMapping insertFunctionMapping, StorageModificationFunctionMapping updateFunctionMapping)76 internal StorageEntityTypeModificationFunctionMapping( 77 EntityType entityType, 78 StorageModificationFunctionMapping deleteFunctionMapping, 79 StorageModificationFunctionMapping insertFunctionMapping, 80 StorageModificationFunctionMapping updateFunctionMapping) 81 { 82 this.EntityType = EntityUtil.CheckArgumentNull(entityType, "entityType"); 83 this.DeleteFunctionMapping = deleteFunctionMapping; 84 this.InsertFunctionMapping = insertFunctionMapping; 85 this.UpdateFunctionMapping = updateFunctionMapping; 86 } 87 88 /// <summary> 89 /// Gets (specific) entity type these functions handle. 90 /// </summary> 91 internal readonly EntityType EntityType; 92 93 /// <summary> 94 /// Gets delete function for the current entity type. 95 /// </summary> 96 internal readonly StorageModificationFunctionMapping DeleteFunctionMapping; 97 98 /// <summary> 99 /// Gets insert function for the current entity type. 100 /// </summary> 101 internal readonly StorageModificationFunctionMapping InsertFunctionMapping; 102 103 /// <summary> 104 /// Gets update function for the current entity type. 105 /// </summary> 106 internal readonly StorageModificationFunctionMapping UpdateFunctionMapping; 107 ToString()108 public override string ToString() 109 { 110 return String.Format(CultureInfo.InvariantCulture, 111 "ET{{{0}}}:{4}DFunc={{{1}}},{4}IFunc={{{2}}},{4}UFunc={{{3}}}", EntityType, DeleteFunctionMapping, 112 InsertFunctionMapping, UpdateFunctionMapping, Environment.NewLine + " "); 113 } 114 Print(int index)115 internal void Print(int index) 116 { 117 StorageEntityContainerMapping.GetPrettyPrintString(ref index); 118 StringBuilder sb = new StringBuilder(); 119 sb.Append("Entity Type Function Mapping"); 120 sb.Append(" "); 121 sb.Append(this.ToString()); 122 Console.WriteLine(sb.ToString()); 123 } 124 } 125 126 /// <summary> 127 /// Describes modification function binding for change processing of entities or associations. 128 /// </summary> 129 internal sealed class StorageModificationFunctionMapping 130 { StorageModificationFunctionMapping( EntitySetBase entitySet, EntityTypeBase entityType, EdmFunction function, IEnumerable<StorageModificationFunctionParameterBinding> parameterBindings, FunctionParameter rowsAffectedParameter, IEnumerable<StorageModificationFunctionResultBinding> resultBindings)131 internal StorageModificationFunctionMapping( 132 EntitySetBase entitySet, 133 EntityTypeBase entityType, 134 EdmFunction function, 135 IEnumerable<StorageModificationFunctionParameterBinding> parameterBindings, 136 FunctionParameter rowsAffectedParameter, 137 IEnumerable<StorageModificationFunctionResultBinding> resultBindings) 138 { 139 EntityUtil.CheckArgumentNull(entitySet, "entitySet"); 140 this.Function = EntityUtil.CheckArgumentNull(function, "function"); 141 this.RowsAffectedParameter = rowsAffectedParameter; 142 this.ParameterBindings = EntityUtil.CheckArgumentNull(parameterBindings, "parameterBindings") 143 .ToList().AsReadOnly(); 144 if (null != resultBindings) 145 { 146 List<StorageModificationFunctionResultBinding> bindings = resultBindings.ToList(); 147 if (0 < bindings.Count) 148 { 149 ResultBindings = bindings.AsReadOnly(); 150 } 151 } 152 this.CollocatedAssociationSetEnds = GetReferencedAssociationSetEnds(entitySet as EntitySet, entityType as EntityType, parameterBindings) 153 .ToList() 154 .AsReadOnly(); 155 } 156 157 /// <summary> 158 /// Gets output parameter producing number of rows affected. May be null. 159 /// </summary> 160 internal readonly FunctionParameter RowsAffectedParameter; 161 162 /// <summary> 163 /// Gets Metadata of function to which we should bind. 164 /// </summary> 165 internal readonly EdmFunction Function; 166 167 /// <summary> 168 /// Gets bindings for function parameters. 169 /// </summary> 170 internal readonly ReadOnlyCollection<StorageModificationFunctionParameterBinding> ParameterBindings; 171 172 /// <summary> 173 /// Gets all association set ends collocated in this mapping. 174 /// </summary> 175 internal readonly ReadOnlyCollection<AssociationSetEnd> CollocatedAssociationSetEnds; 176 177 /// <summary> 178 /// Gets bindings for the results of function evaluation. 179 /// </summary> 180 internal readonly ReadOnlyCollection<StorageModificationFunctionResultBinding> ResultBindings; 181 ToString()182 public override string ToString() 183 { 184 return String.Format(CultureInfo.InvariantCulture, 185 "Func{{{0}}}: Prm={{{1}}}, Result={{{2}}}", Function, 186 StringUtil.ToCommaSeparatedStringSorted(ParameterBindings), 187 StringUtil.ToCommaSeparatedStringSorted(ResultBindings)); 188 } 189 190 // requires: entitySet must not be null 191 // Yields all referenced association set ends in this mapping. GetReferencedAssociationSetEnds(EntitySet entitySet, EntityType entityType, IEnumerable<StorageModificationFunctionParameterBinding> parameterBindings)192 private static IEnumerable<AssociationSetEnd> GetReferencedAssociationSetEnds(EntitySet entitySet, EntityType entityType, IEnumerable<StorageModificationFunctionParameterBinding> parameterBindings) 193 { 194 HashSet<AssociationSetEnd> ends = new HashSet<AssociationSetEnd>(); 195 if (null != entitySet && null != entityType) 196 { 197 foreach (StorageModificationFunctionParameterBinding parameterBinding in parameterBindings) 198 { 199 AssociationSetEnd end = parameterBinding.MemberPath.AssociationSetEnd; 200 if (null != end) 201 { 202 ends.Add(end); 203 } 204 } 205 206 // If there is a referential constraint, it counts as an implicit mapping of 207 // the association set 208 foreach (AssociationSet assocationSet in MetadataHelper.GetAssociationsForEntitySet(entitySet)) 209 { 210 ReadOnlyMetadataCollection<ReferentialConstraint> constraints = assocationSet.ElementType.ReferentialConstraints; 211 if (null != constraints) 212 { 213 foreach (ReferentialConstraint constraint in constraints) 214 { 215 if ((assocationSet.AssociationSetEnds[constraint.ToRole.Name].EntitySet == entitySet) && 216 (constraint.ToRole.GetEntityType().IsAssignableFrom(entityType))) 217 { 218 ends.Add(assocationSet.AssociationSetEnds[constraint.FromRole.Name]); 219 } 220 } 221 } 222 } 223 } 224 return ends; 225 } 226 } 227 228 /// <summary> 229 /// Defines a binding from a named result set column to a member taking the value. 230 /// </summary> 231 internal sealed class StorageModificationFunctionResultBinding 232 { StorageModificationFunctionResultBinding(string columnName, EdmProperty property)233 internal StorageModificationFunctionResultBinding(string columnName, EdmProperty property) 234 { 235 this.ColumnName = EntityUtil.CheckArgumentNull(columnName, "columnName"); 236 this.Property = EntityUtil.CheckArgumentNull(property, "property"); 237 } 238 239 /// <summary> 240 /// Gets the name of the column to bind from the function result set. We use a string 241 /// value rather than EdmMember, since there is no metadata for function result sets. 242 /// </summary> 243 internal readonly string ColumnName; 244 245 /// <summary> 246 /// Gets the property to be set on the entity. 247 /// </summary> 248 internal readonly EdmProperty Property; 249 ToString()250 public override string ToString() 251 { 252 return String.Format(CultureInfo.InvariantCulture, 253 "{0}->{1}", ColumnName, Property); 254 } 255 } 256 257 /// <summary> 258 /// Binds a modification function parameter to a member of the entity or association being modified. 259 /// </summary> 260 internal sealed class StorageModificationFunctionParameterBinding 261 { StorageModificationFunctionParameterBinding(FunctionParameter parameter, StorageModificationFunctionMemberPath memberPath, bool isCurrent)262 internal StorageModificationFunctionParameterBinding(FunctionParameter parameter, StorageModificationFunctionMemberPath memberPath, bool isCurrent) 263 { 264 this.Parameter = EntityUtil.CheckArgumentNull(parameter, "parameter"); 265 this.MemberPath = EntityUtil.CheckArgumentNull(memberPath, "memberPath"); 266 this.IsCurrent = isCurrent; 267 } 268 269 /// <summary> 270 /// Gets the parameter taking the value. 271 /// </summary> 272 internal readonly FunctionParameter Parameter; 273 274 /// <summary> 275 /// Gets the path to the entity or association member defining the value. 276 /// </summary> 277 internal readonly StorageModificationFunctionMemberPath MemberPath; 278 279 /// <summary> 280 /// Gets a value indicating whether the current or original 281 /// member value is being bound. 282 /// </summary> 283 internal readonly bool IsCurrent; 284 ToString()285 public override string ToString() 286 { 287 return String.Format(CultureInfo.InvariantCulture, 288 "@{0}->{1}{2}", Parameter, IsCurrent ? "+" : "-", MemberPath); 289 } 290 } 291 292 /// <summary> 293 /// Describes the location of a member within an entity or association type structure. 294 /// </summary> 295 internal sealed class StorageModificationFunctionMemberPath 296 { StorageModificationFunctionMemberPath(IEnumerable<EdmMember> members, AssociationSet associationSetNavigation)297 internal StorageModificationFunctionMemberPath(IEnumerable<EdmMember> members, AssociationSet associationSetNavigation) 298 { 299 this.Members = new ReadOnlyCollection<EdmMember>(new List<EdmMember>( 300 EntityUtil.CheckArgumentNull(members, "members"))); 301 if (null != associationSetNavigation) 302 { 303 Debug.Assert(2 == this.Members.Count, "Association bindings must always consist of the end and the key"); 304 305 // find the association set end 306 this.AssociationSetEnd = associationSetNavigation.AssociationSetEnds[this.Members[1].Name]; 307 } 308 } 309 310 /// <summary> 311 /// Gets the members in the path from the leaf (the member being bound) 312 /// to the Root of the structure. 313 /// </summary> 314 internal readonly ReadOnlyCollection<EdmMember> Members; 315 316 /// <summary> 317 /// Gets the association set to which we are navigating via this member. If the value 318 /// is null, this is not a navigation member path. 319 /// </summary> 320 internal readonly AssociationSetEnd AssociationSetEnd; 321 ToString()322 public override string ToString() 323 { 324 return String.Format(CultureInfo.InvariantCulture, "{0}{1}", 325 null == AssociationSetEnd ? String.Empty : "[" + AssociationSetEnd.ParentAssociationSet.ToString() + "]", 326 StringUtil.BuildDelimitedList(Members, null, ".")); 327 } 328 } 329 } 330