1 //--------------------------------------------------------------------- 2 // <copyright file="ValueExpressions.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 namespace System.Data.Common.CommandTrees 11 { 12 using System; 13 using System.Collections.Generic; 14 using System.Data.Common; 15 using System.Data.Common.CommandTrees.Internal; 16 using System.Data.Metadata.Edm; 17 using System.Diagnostics; 18 19 /// <summary> 20 /// Represents a constant value. 21 /// </summary> 22 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 23 public sealed class DbConstantExpression : DbExpression 24 { 25 private readonly bool _shouldCloneValue; 26 private readonly object _value; 27 DbConstantExpression(TypeUsage resultType, object value)28 internal DbConstantExpression(TypeUsage resultType, object value) 29 : base(DbExpressionKind.Constant, resultType) 30 { 31 Debug.Assert(value != null, "DbConstantExpression value cannot be null"); 32 Debug.Assert(TypeSemantics.IsScalarType(resultType), "DbConstantExpression must have a primitive or enum value"); 33 Debug.Assert(!value.GetType().IsEnum || TypeSemantics.IsEnumerationType(resultType), "value is an enum while the result type is not of enum type."); 34 Debug.Assert(Helper.AsPrimitive(resultType.EdmType).ClrEquivalentType == (value.GetType().IsEnum ? value.GetType().GetEnumUnderlyingType() : value.GetType()), 35 "the type of the value has to match the result type (for enum types only underlying types are compared)."); 36 37 // binary values should be cloned before use 38 PrimitiveType primitiveType; 39 this._shouldCloneValue = TypeHelpers.TryGetEdmType<PrimitiveType>(resultType, out primitiveType) 40 && primitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Binary; 41 42 if (this._shouldCloneValue) 43 { 44 // DevDiv#480416: DbConstantExpression with a binary value is not fully immutable 45 // 46 this._value = ((byte[])value).Clone(); 47 } 48 else 49 { 50 this._value = value; 51 } 52 } 53 54 /// <summary> 55 /// Provides direct access to the constant value, even for byte[] constants. 56 /// </summary> 57 /// <returns>The object value contained by this constant expression, not a copy.</returns> GetValue()58 internal object GetValue() 59 { 60 return this._value; 61 } 62 63 /// <summary> 64 /// Gets the constant value. 65 /// </summary> 66 public object Value 67 { 68 get 69 { 70 // DevDiv#480416: DbConstantExpression with a binary value is not fully immutable 71 // 72 if (this._shouldCloneValue) 73 { 74 return ((byte[])_value).Clone(); 75 } 76 else 77 { 78 return _value; 79 } 80 } 81 } 82 83 /// <summary> 84 /// The visitor pattern method for expression visitors that do not produce a result value. 85 /// </summary> 86 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 87 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)88 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 89 90 /// <summary> 91 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 92 /// </summary> 93 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 94 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 95 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 96 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)97 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 98 } 99 100 /// <summary> 101 /// Represents null. 102 /// </summary> 103 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 104 public sealed class DbNullExpression : DbExpression 105 { DbNullExpression(TypeUsage type)106 internal DbNullExpression(TypeUsage type) 107 : base(DbExpressionKind.Null, type) 108 { 109 } 110 111 /// <summary> 112 /// The visitor pattern method for expression visitors that do not produce a result value. 113 /// </summary> 114 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 115 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)116 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 117 118 /// <summary> 119 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 120 /// </summary> 121 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 122 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 123 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 124 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)125 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 126 } 127 128 /// <summary> 129 /// Represents a reference to a variable that is currently in scope. 130 /// </summary> 131 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 132 public sealed class DbVariableReferenceExpression : DbExpression 133 { 134 private readonly string _name; 135 DbVariableReferenceExpression(TypeUsage type, string name)136 internal DbVariableReferenceExpression(TypeUsage type, string name) 137 : base(DbExpressionKind.VariableReference, type) 138 { 139 Debug.Assert(name != null, "DbVariableReferenceExpression Name cannot be null"); 140 141 this._name = name; 142 } 143 144 /// <summary> 145 /// Gets the name of the referenced variable. 146 /// </summary> 147 public string VariableName { get { return _name; } } 148 149 /// <summary> 150 /// The visitor pattern method for expression visitors that do not produce a result value. 151 /// </summary> 152 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 153 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)154 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 155 156 /// <summary> 157 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 158 /// </summary> 159 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 160 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 161 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 162 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)163 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 164 } 165 166 /// <summary> 167 /// Represents a reference to a parameter declared on the command tree that contains this expression. 168 /// </summary> 169 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 170 public sealed class DbParameterReferenceExpression : DbExpression 171 { 172 private readonly string _name; 173 DbParameterReferenceExpression(TypeUsage type, string name)174 internal DbParameterReferenceExpression(TypeUsage type, string name) 175 : base(DbExpressionKind.ParameterReference, type) 176 { 177 Debug.Assert(DbCommandTree.IsValidParameterName(name), "DbParameterReferenceExpression name should be valid"); 178 179 this._name = name; 180 } 181 182 /// <summary> 183 /// Gets the name of the referenced parameter. 184 /// </summary> 185 public string ParameterName { get { return _name; } } 186 187 /// <summary> 188 /// The visitor pattern method for expression visitors that do not produce a result value. 189 /// </summary> 190 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 191 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)192 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 193 194 /// <summary> 195 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 196 /// </summary> 197 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 198 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 199 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 200 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)201 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 202 } 203 204 /// <summary> 205 /// Represents the retrieval of a static or instance property. 206 /// </summary> 207 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 208 public sealed class DbPropertyExpression : DbExpression 209 { 210 private readonly EdmMember _property; 211 private readonly DbExpression _instance; 212 DbPropertyExpression(TypeUsage resultType, EdmMember property, DbExpression instance)213 internal DbPropertyExpression(TypeUsage resultType, EdmMember property, DbExpression instance) 214 : base(DbExpressionKind.Property, resultType) 215 { 216 Debug.Assert(property != null, "DbPropertyExpression property cannot be null"); 217 Debug.Assert(instance != null, "DbPropertyExpression instance cannot be null"); 218 Debug.Assert(Helper.IsEdmProperty(property) || 219 Helper.IsRelationshipEndMember(property) || 220 Helper.IsNavigationProperty(property), "DbExpression property must be a property, navigation property, or relationship end"); 221 222 this._property = property; 223 this._instance = instance; 224 } 225 226 /// <summary> 227 /// Gets the property metadata for the property to retrieve. 228 /// </summary> 229 public EdmMember Property { get { return _property; } } 230 231 /// <summary> 232 /// Gets the <see cref="DbExpression"/> that defines the instance from which the property should be retrieved. 233 /// </summary> 234 public DbExpression Instance { get { return _instance; } } 235 236 /// <summary> 237 /// The visitor pattern method for expression visitors that do not produce a result value. 238 /// </summary> 239 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 240 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)241 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 242 243 /// <summary> 244 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 245 /// </summary> 246 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 247 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 248 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 249 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)250 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 251 252 /// <summary> 253 /// Creates a new KeyValuePair<string, DbExpression> based on this property expression. 254 /// The string key will be the name of the referenced property, while the DbExpression value will be the property expression itself. 255 /// </summary> 256 /// <returns>A new KeyValuePair<string, DbExpression> with key and value derived from the DbPropertyExpression</returns> ToKeyValuePair()257 public KeyValuePair<string, DbExpression> ToKeyValuePair() 258 { 259 return new KeyValuePair<string, DbExpression>(this.Property.Name, this); 260 } 261 operator KeyValuePair<string, DbExpression>(DbPropertyExpression value)262 public static implicit operator KeyValuePair<string, DbExpression>(DbPropertyExpression value) 263 { 264 EntityUtil.CheckArgumentNull(value, "value"); 265 return value.ToKeyValuePair(); 266 } 267 } 268 269 /// <summary> 270 /// Represents the invocation of a function. 271 /// </summary> 272 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 273 public sealed class DbFunctionExpression : DbExpression 274 { 275 private readonly EdmFunction _functionInfo; 276 private readonly DbExpressionList _arguments; 277 DbFunctionExpression(TypeUsage resultType, EdmFunction function, DbExpressionList arguments)278 internal DbFunctionExpression(TypeUsage resultType, EdmFunction function, DbExpressionList arguments) 279 : base(DbExpressionKind.Function, resultType) 280 { 281 Debug.Assert(function != null, "DbFunctionExpression function cannot be null"); 282 Debug.Assert(arguments != null, "DbFunctionExpression arguments cannot be null"); 283 Debug.Assert(object.ReferenceEquals(resultType, function.ReturnParameter.TypeUsage), "DbFunctionExpression result type must be function return type"); 284 285 this._functionInfo = function; 286 this._arguments = arguments; 287 } 288 289 /// <summary> 290 /// Gets the metadata for the function to invoke. 291 /// </summary> 292 public EdmFunction Function { get { return _functionInfo; } } 293 294 /// <summary> 295 /// Gets an <see cref="DbExpression"/> list that provides the arguments to the function. 296 /// </summary> 297 public IList<DbExpression> Arguments { get { return this._arguments; } } 298 299 /// <summary> 300 /// The visitor pattern method for expression visitors that do not produce a result value. 301 /// </summary> 302 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 303 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)304 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 305 306 /// <summary> 307 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 308 /// </summary> 309 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 310 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 311 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 312 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)313 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 314 } 315 316 /// <summary> 317 /// Represents the application of a Lambda function. 318 /// </summary> 319 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 320 public sealed class DbLambdaExpression : DbExpression 321 { 322 private readonly DbLambda _lambda; 323 private readonly DbExpressionList _arguments; 324 DbLambdaExpression(TypeUsage resultType, DbLambda lambda, DbExpressionList args)325 internal DbLambdaExpression(TypeUsage resultType, DbLambda lambda, DbExpressionList args) 326 : base(DbExpressionKind.Lambda, resultType) 327 { 328 Debug.Assert(lambda != null, "DbLambdaExpression lambda cannot be null"); 329 Debug.Assert(args != null, "DbLambdaExpression arguments cannot be null"); 330 Debug.Assert(object.ReferenceEquals(resultType, lambda.Body.ResultType), "DbLambdaExpression result type must be Lambda body result type"); 331 Debug.Assert(lambda.Variables.Count == args.Count, "DbLambdaExpression argument count does not match Lambda parameter count"); 332 333 this._lambda = lambda; 334 this._arguments = args; 335 } 336 337 /// <summary> 338 /// Gets the <see cref="DbLambda"/> representing the Lambda function applied by this expression. 339 /// </summary> 340 public DbLambda Lambda { get { return _lambda; } } 341 342 /// <summary> 343 /// Gets a <see cref="DbExpression"/> list that provides the arguments to which the Lambda function should be applied. 344 /// </summary> 345 public IList<DbExpression> Arguments { get { return this._arguments; } } 346 347 /// <summary> 348 /// The visitor pattern method for expression visitors that do not produce a result value. 349 /// </summary> 350 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 351 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)352 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 353 354 /// <summary> 355 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 356 /// </summary> 357 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 358 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 359 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 360 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)361 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 362 } 363 364 #if METHOD_EXPRESSION 365 /// <summary> 366 /// Represents the invocation of a method. 367 /// </summary> 368 public sealed class MethodExpression : Expression 369 { 370 MethodMetadata m_methodInfo; 371 IList<Expression> m_args; 372 ExpressionLink m_inst; 373 MethodExpression(CommandTree cmdTree, MethodMetadata methodInfo, IList<Expression> args, Expression instance)374 internal MethodExpression(CommandTree cmdTree, MethodMetadata methodInfo, IList<Expression> args, Expression instance) 375 : base(cmdTree, ExpressionKind.Method) 376 { 377 // 378 // Ensure that the property metadata is non-null and from the same metadata workspace and dataspace as the command tree. 379 // 380 CommandTreeTypeHelper.CheckMember(methodInfo, "method", "methodInfo"); 381 382 // 383 // Methods that return void are not allowed 384 // 385 if (cmdTree.TypeHelper.IsNullOrNullType(methodInfo.Type)) 386 { 387 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Method_VoidResultInvalid, "methodInfo"); 388 } 389 390 if (null == args) 391 { 392 throw EntityUtil.ArgumentNull("args"); 393 } 394 395 this.m_inst = new ExpressionLink("Instance", cmdTree); 396 397 // 398 // Validate the instance 399 // 400 if (methodInfo.IsStatic) 401 { 402 if (instance != null) 403 { 404 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Method_InstanceInvalidForStatic, "instance"); 405 } 406 } 407 else 408 { 409 if (null == instance) 410 { 411 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Method_InstanceRequiredForInstance, "instance"); 412 } 413 414 this.m_inst.SetExpectedType(methodInfo.DefiningType); 415 this.m_inst.InitializeValue(instance); 416 } 417 418 // 419 // Validate the arguments 420 // 421 m_args = new ExpressionList("Arguments", cmdTree, methodInfo.Parameters, args); 422 m_methodInfo = methodInfo; 423 this.ResultType = methodInfo.Type; 424 } 425 426 /// <summary> 427 /// Gets the metadata for the method to invoke. 428 /// </summary> 429 public MethodMetadata Method { get { return m_methodInfo; } } 430 431 /// <summary> 432 /// Gets the expressions that provide the arguments to the method. 433 /// </summary> 434 public IList<Expression> Arguments { get { return m_args; } } 435 436 /// <summary> 437 /// Gets or sets an <see cref="Expression"/> that defines the instance on which the method should be invoked. Must be null for instance methods. 438 /// For static properties, <code>Instance</code> will always be null, and attempting to set a new value will result 439 /// in <see cref="NotSupportedException"/>. 440 /// </summary> 441 /// <exception cref="ArgumentNullException">The expression is null</exception> 442 /// <exception cref="NotSupportedException">The method is static</exception> 443 /// <exception cref="ArgumentException"> 444 /// The expression is not associated with the MethodExpression's command tree, 445 /// or its result type is not equal or promotable to the type that defines the method 446 /// </exception> 447 public Expression Instance 448 { 449 get { return m_inst.Expression; } 450 /*CQT_PUBLIC_API(*/internal/*)*/ set 451 { 452 if (this.m_methodInfo.IsStatic) 453 { 454 throw EntityUtil.NotSupported(System.Data.Entity.Strings.Cqt_Method_InstanceInvalidForStatic); 455 } 456 457 this.m_inst.Expression = value; 458 } 459 } 460 461 /// <summary> 462 /// The visitor pattern method for expression visitors that do not produce a result value. 463 /// </summary> 464 /// <param name="visitor">An instance of ExpressionVisitor.</param> 465 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(ExpressionVisitor visitor)466 public override void Accept(ExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 467 468 /// <summary> 469 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 470 /// </summary> 471 /// <param name="visitor">An instance of a typed ExpressionVisitor that produces a result value of type TResultType.</param> 472 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 473 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 474 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)475 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 476 } 477 #endif 478 479 /// <summary> 480 /// Represents the navigation of a (composition or association) relationship given the 'from' role, the 'to' role and an instance of the from role 481 /// </summary> 482 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 483 public sealed class DbRelationshipNavigationExpression : DbExpression 484 { 485 private readonly RelationshipType _relation; 486 private readonly RelationshipEndMember _fromRole; 487 private readonly RelationshipEndMember _toRole; 488 private readonly DbExpression _from; 489 DbRelationshipNavigationExpression( TypeUsage resultType, RelationshipType relType, RelationshipEndMember fromEnd, RelationshipEndMember toEnd, DbExpression navigateFrom)490 internal DbRelationshipNavigationExpression( 491 TypeUsage resultType, 492 RelationshipType relType, 493 RelationshipEndMember fromEnd, 494 RelationshipEndMember toEnd, 495 DbExpression navigateFrom) 496 : base(DbExpressionKind.RelationshipNavigation, resultType) 497 { 498 Debug.Assert(relType != null, "DbRelationshipNavigationExpression relationship type cannot be null"); 499 Debug.Assert(fromEnd != null, "DbRelationshipNavigationExpression 'from' end cannot be null"); 500 Debug.Assert(toEnd != null, "DbRelationshipNavigationExpression 'to' end cannot be null"); 501 Debug.Assert(navigateFrom != null, "DbRelationshipNavigationExpression navigation source cannot be null"); 502 503 this._relation = relType; 504 this._fromRole = fromEnd; 505 this._toRole = toEnd; 506 this._from = navigateFrom; 507 } 508 509 /// <summary> 510 /// Gets the metadata for the relationship over which navigation occurs 511 /// </summary> 512 public RelationshipType Relationship { get { return _relation; } } 513 514 /// <summary> 515 /// Gets the metadata for the relationship end to navigate from 516 /// </summary> 517 public RelationshipEndMember NavigateFrom { get { return _fromRole; } } 518 519 /// <summary> 520 /// Gets the metadata for the relationship end to navigate to 521 /// </summary> 522 public RelationshipEndMember NavigateTo { get { return _toRole; } } 523 524 /// <summary> 525 /// Gets the <see cref="DbExpression"/> that specifies the instance of the 'from' relationship end from which navigation should occur. 526 /// </summary> 527 public DbExpression NavigationSource { get { return _from; } } 528 529 /// <summary> 530 /// The visitor pattern method for expression visitors that do not produce a result value. 531 /// </summary> 532 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 533 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)534 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 535 536 /// <summary> 537 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 538 /// </summary> 539 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 540 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 541 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 542 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)543 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 544 } 545 546 /// <summary> 547 /// Encapsulates the result (represented as a Ref to the resulting Entity) of navigating from 548 /// the specified source end of a relationship to the specified target end. This class is intended 549 /// for use only with <see cref="DbNewInstanceExpression"/>, where an 'owning' instance of that class 550 /// represents the source Entity involved in the relationship navigation. 551 /// Instances of DbRelatedEntityRef may be specified when creating a <see cref="DbNewInstanceExpression"/> that 552 /// constructs an Entity, allowing information about Entities that are related to the newly constructed Entity to be captured. 553 /// </summary> 554 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 555 internal sealed class DbRelatedEntityRef 556 { 557 private readonly RelationshipEndMember _sourceEnd; 558 private readonly RelationshipEndMember _targetEnd; 559 private readonly DbExpression _targetEntityRef; 560 DbRelatedEntityRef(RelationshipEndMember sourceEnd, RelationshipEndMember targetEnd, DbExpression targetEntityRef)561 internal DbRelatedEntityRef(RelationshipEndMember sourceEnd, RelationshipEndMember targetEnd, DbExpression targetEntityRef) 562 { 563 // Validate that the specified relationship ends are: 564 // 1. Non-null 565 // 2. From the same metadata workspace as that used by the command tree 566 EntityUtil.CheckArgumentNull(sourceEnd, "sourceEnd"); 567 EntityUtil.CheckArgumentNull(targetEnd, "targetEnd"); 568 569 // Validate that the specified target entity ref is: 570 // 1. Non-null 571 EntityUtil.CheckArgumentNull(targetEntityRef, "targetEntityRef"); 572 573 // Validate that the specified source and target ends are: 574 // 1. Declared by the same relationship type 575 if (!object.ReferenceEquals(sourceEnd.DeclaringType, targetEnd.DeclaringType)) 576 { 577 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelatedEntityRef_TargetEndFromDifferentRelationship, "targetEnd"); 578 } 579 // 2. Not the same end 580 if (object.ReferenceEquals(sourceEnd, targetEnd)) 581 { 582 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelatedEntityRef_TargetEndSameAsSourceEnd, "targetEnd"); 583 } 584 585 // Validate that the specified target end has multiplicity of at most one 586 if (targetEnd.RelationshipMultiplicity != RelationshipMultiplicity.One && 587 targetEnd.RelationshipMultiplicity != RelationshipMultiplicity.ZeroOrOne) 588 { 589 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelatedEntityRef_TargetEndMustBeAtMostOne, "targetEnd"); 590 } 591 592 // Validate that the specified target entity ref actually has a ref result type 593 if (!TypeSemantics.IsReferenceType(targetEntityRef.ResultType)) 594 { 595 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelatedEntityRef_TargetEntityNotRef, "targetEntityRef"); 596 } 597 598 // Validate that the specified target entity is of a type that can be reached by navigating to the specified relationship end 599 EntityTypeBase endType = TypeHelpers.GetEdmType<RefType>(targetEnd.TypeUsage).ElementType; 600 EntityTypeBase targetType = TypeHelpers.GetEdmType<RefType>(targetEntityRef.ResultType).ElementType; 601 // 602 if (!endType.EdmEquals(targetType) && !TypeSemantics.IsSubTypeOf(targetType, endType)) 603 { 604 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelatedEntityRef_TargetEntityNotCompatible, "targetEntityRef"); 605 } 606 607 // Validation succeeded, initialize state 608 _targetEntityRef = targetEntityRef; 609 _targetEnd = targetEnd; 610 _sourceEnd = sourceEnd; 611 } 612 613 /// <summary> 614 /// Retrieves the 'source' end of the relationship navigation satisfied by this related entity Ref 615 /// </summary> 616 internal RelationshipEndMember SourceEnd { get { return _sourceEnd; } } 617 618 /// <summary> 619 /// Retrieves the 'target' end of the relationship navigation satisfied by this related entity Ref 620 /// </summary> 621 internal RelationshipEndMember TargetEnd { get { return _targetEnd; } } 622 623 /// <summary> 624 /// Retrieves the entity Ref that is the result of navigating from the source to the target end of this related entity Ref 625 /// </summary> 626 internal DbExpression TargetEntityReference { get { return _targetEntityRef; } } 627 } 628 629 /// <summary> 630 /// Represents the construction of a new instance of a given type, including set and record types. 631 /// </summary> 632 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 633 public sealed class DbNewInstanceExpression : DbExpression 634 { 635 private readonly DbExpressionList _elements; 636 private readonly System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef> _relatedEntityRefs; 637 DbNewInstanceExpression(TypeUsage type, DbExpressionList args)638 internal DbNewInstanceExpression(TypeUsage type, DbExpressionList args) 639 : base(DbExpressionKind.NewInstance, type) 640 { 641 Debug.Assert(args != null, "DbNewInstanceExpression arguments cannot be null"); 642 Debug.Assert(args.Count > 0 || TypeSemantics.IsCollectionType(type), "DbNewInstanceExpression requires at least one argument when not creating an empty collection"); 643 644 this._elements = args; 645 } 646 DbNewInstanceExpression(TypeUsage resultType, DbExpressionList attributeValues, System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef> relationships)647 internal DbNewInstanceExpression(TypeUsage resultType, DbExpressionList attributeValues, System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef> relationships) 648 : this(resultType, attributeValues) 649 { 650 Debug.Assert(TypeSemantics.IsEntityType(resultType), "An entity type is required to create a NewEntityWithRelationships expression"); 651 Debug.Assert(relationships != null, "Related entity ref collection cannot be null"); 652 653 this._relatedEntityRefs = (relationships.Count > 0 ? relationships : null); 654 } 655 656 /// <summary> 657 /// Gets an <see cref="DbExpression"/> list that provides the property/column values or set elements for the new instance. 658 /// </summary> 659 public IList<DbExpression> Arguments { get { return _elements; } } 660 661 /// <summary> 662 /// The visitor pattern method for expression visitors that do not produce a result value. 663 /// </summary> 664 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 665 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)666 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 667 668 /// <summary> 669 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 670 /// </summary> 671 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 672 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 673 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 674 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)675 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 676 677 678 internal bool HasRelatedEntityReferences { get { return (_relatedEntityRefs != null); } } 679 680 /// <summary> 681 /// Gets the related entity references (if any) for an entity constructor. 682 /// May be null if no related entities were specified - use the <see cref="HasRelatedEntityReferences"/> property to determine this. 683 /// </summary> 684 internal System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef> RelatedEntityReferences { get { return _relatedEntityRefs; } } 685 } 686 687 /// <summary> 688 /// Represents a (strongly typed) reference to a specific instance within a given entity set. 689 /// </summary> 690 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 691 public sealed class DbRefExpression : DbUnaryExpression 692 { 693 private readonly EntitySet _entitySet; 694 DbRefExpression(TypeUsage refResultType, EntitySet entitySet, DbExpression refKeys)695 internal DbRefExpression(TypeUsage refResultType, EntitySet entitySet, DbExpression refKeys) 696 : base(DbExpressionKind.Ref, refResultType, refKeys) 697 { 698 Debug.Assert(TypeSemantics.IsReferenceType(refResultType), "DbRefExpression requires a reference result type"); 699 700 this._entitySet = entitySet; 701 } 702 703 /// <summary> 704 /// Gets the metadata for the entity set that contains the instance. 705 /// </summary> 706 public EntitySet EntitySet { get { return _entitySet; } } 707 708 /// <summary> 709 /// The visitor pattern method for expression visitors that do not produce a result value. 710 /// </summary> 711 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 712 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)713 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 714 715 /// <summary> 716 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 717 /// </summary> 718 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 719 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 720 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 721 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)722 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 723 } 724 725 /// <summary> 726 /// Represents the retrieval of a given entity using the specified Ref. 727 /// </summary> 728 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Deref"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 729 public sealed class DbDerefExpression : DbUnaryExpression 730 { DbDerefExpression(TypeUsage entityResultType, DbExpression refExpr)731 internal DbDerefExpression(TypeUsage entityResultType, DbExpression refExpr) 732 : base(DbExpressionKind.Deref, entityResultType, refExpr) 733 { 734 Debug.Assert(TypeSemantics.IsEntityType(entityResultType), "DbDerefExpression requires an entity result type"); 735 } 736 737 /// <summary> 738 /// The visitor pattern method for expression visitors that do not produce a result value. 739 /// </summary> 740 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 741 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)742 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 743 744 /// <summary> 745 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 746 /// </summary> 747 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 748 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 749 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 750 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)751 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 752 } 753 754 /// <summary> 755 /// Represents a 'scan' of all elements of a given entity set. 756 /// </summary> 757 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] 758 public sealed class DbScanExpression : DbExpression 759 { 760 private readonly EntitySetBase _targetSet; 761 DbScanExpression(TypeUsage collectionOfEntityType, EntitySetBase entitySet)762 internal DbScanExpression(TypeUsage collectionOfEntityType, EntitySetBase entitySet) 763 : base(DbExpressionKind.Scan, collectionOfEntityType) 764 { 765 Debug.Assert(entitySet != null, "DbScanExpression entity set cannot be null"); 766 767 this._targetSet = entitySet; 768 } 769 770 /// <summary> 771 /// Gets the metadata for the referenced entity or relationship set. 772 /// </summary> 773 public EntitySetBase Target { get { return _targetSet; } } 774 775 /// <summary> 776 /// The visitor pattern method for expression visitors that do not produce a result value. 777 /// </summary> 778 /// <param name="visitor">An instance of DbExpressionVisitor.</param> 779 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> Accept(DbExpressionVisitor visitor)780 public override void Accept(DbExpressionVisitor visitor) { if (visitor != null) { visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 781 782 /// <summary> 783 /// The visitor pattern method for expression visitors that produce a result value of a specific type. 784 /// </summary> 785 /// <param name="visitor">An instance of a typed DbExpressionVisitor that produces a result value of type TResultType.</param> 786 /// <typeparam name="TResultType">The type of the result produced by <paramref name="visitor"/></typeparam> 787 /// <exception cref="ArgumentNullException"><paramref name="visitor"/> is null</exception> 788 /// <returns>An instance of <typeparamref name="TResultType"/>.</returns> Accept(DbExpressionVisitor<TResultType> visitor)789 public override TResultType Accept<TResultType>(DbExpressionVisitor<TResultType> visitor) { if (visitor != null) { return visitor.Visit(this); } else { throw EntityUtil.ArgumentNull("visitor"); } } 790 } 791 } 792