1 //---------------------------------------------------------------------
2 // <copyright file="EntityParameter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 namespace System.Data.EntityClient
12 using System.Data.Common;
13 using System.Data.Common.CommandTrees;
14 using System.Data.Common.Internal;
15 using System.Data.Metadata.Edm;
16 using System.Diagnostics;
19 /// Class representing a parameter used in EntityCommand
21 public sealed partial class EntityParameter : DbParameter, IDbDataParameter
23 private string _parameterName;
24 private DbType? _dbType;
25 private EdmType _edmType;
26 private byte? _precision;
28 private bool _isDirty;
31 /// Constructs the EntityParameter object
33 public EntityParameter()
38 /// Constructs the EntityParameter object with the given parameter name and the type of the parameter
40 /// <param name="parameterName">The name of the parameter</param>
41 /// <param name="dbType">The type of the parameter</param>
42 public EntityParameter(string parameterName, DbType dbType)
44 SetParameterNameWithValidation(parameterName, "parameterName");
49 /// Constructs the EntityParameter object with the given parameter name, the type of the parameter, and the size of the
52 /// <param name="parameterName">The name of the parameter</param>
53 /// <param name="dbType">The type of the parameter</param>
54 /// <param name="size">The size of the parameter</param>
55 public EntityParameter(string parameterName, DbType dbType, int size)
57 SetParameterNameWithValidation(parameterName, "parameterName");
63 /// Constructs the EntityParameter object with the given parameter name, the type of the parameter, the size of the
64 /// parameter, and the name of the source column
66 /// <param name="parameterName">The name of the parameter</param>
67 /// <param name="dbType">The type of the parameter</param>
68 /// <param name="size">The size of the parameter</param>
69 /// <param name="sourceColumn">The name of the source column mapped to the data set, used for loading the parameter value</param>
70 public EntityParameter(string parameterName, DbType dbType, int size, string sourceColumn)
72 SetParameterNameWithValidation(parameterName, "parameterName");
75 this.SourceColumn = sourceColumn;
79 /// Constructs the EntityParameter object with the given parameter name, the type of the parameter, the size of the
80 /// parameter, and the name of the source column
82 /// <param name="parameterName">The name of the parameter</param>
83 /// <param name="dbType">The type of the parameter</param>
84 /// <param name="size">The size of the parameter</param>
85 /// <param name="direction">The direction of the parameter, whether it's input/output/both/return value</param>
86 /// <param name="isNullable">If the parameter is nullable</param>
87 /// <param name="precision">The floating point precision of the parameter, valid only if the parameter type is a floating point type</param>
88 /// <param name="scale">The scale of the parameter, valid only if the parameter type is a floating point type</param>
89 /// <param name="sourceColumn">The name of the source column mapped to the data set, used for loading the parameter value</param>
90 /// <param name="sourceVersion">The data row version to use when loading the parameter value</param>
91 /// <param name="value">The value of the parameter</param>
92 public EntityParameter(string parameterName,
95 ParameterDirection direction,
100 DataRowVersion sourceVersion,
103 SetParameterNameWithValidation(parameterName, "parameterName");
104 this.DbType = dbType;
106 this.Direction = direction;
107 this.IsNullable = isNullable;
108 this.Precision = precision;
110 this.SourceColumn = sourceColumn;
111 this.SourceVersion = sourceVersion;
116 /// The name of the parameter
118 public override string ParameterName
122 return this._parameterName ?? "";
126 SetParameterNameWithValidation(value, "value");
131 /// Helper method to validate the parameter name; Ideally we'd only call this once, but
132 /// we have to put an argumentName on the Argument exception, and the property setter would
133 /// need "value" which confuses folks when they call the constructor that takes the value
134 /// of the parameter. c'est la vie.
136 /// <param name="parameterName"></param>
137 /// <param name="argumentName"></param>
138 private void SetParameterNameWithValidation(string parameterName, string argumentName)
140 if (!string.IsNullOrEmpty(parameterName) && !DbCommandTree.IsValidParameterName(parameterName))
142 throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_InvalidParameterName(parameterName), argumentName);
145 this._parameterName = parameterName;
149 /// The type of the parameter, EdmType may also be set, and may provide more detailed information.
151 public override DbType DbType
155 // if the user has not set the dbType but has set the dbType, use the edmType to try to deduce a dbType
156 if (!this._dbType.HasValue)
158 if (this._edmType != null)
160 return GetDbTypeFromEdm(_edmType);
162 // If the user has set neither the DbType nor the EdmType,
163 // then we attempt to deduce it from the value, but we won't set it in the
164 // member field as that's used to keep track of what the user set explicitly
167 // If we can't deduce the type because there are no values, we still have to return something, just assume it's string type
169 return DbType.String;
173 return TypeHelpers.ConvertClrTypeToDbType(_value.GetType());
175 catch (ArgumentException e)
177 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotDeduceDbType, e);
182 return (DbType)this._dbType;
187 this._dbType = value;
192 /// The type of the parameter, expressed as an EdmType.
193 /// May be null (which is what it will be if unset). This means
194 /// that the DbType contains all the type information.
195 /// Non-null values must not contradict DbType (only restate or specialize).
197 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
198 public EdmType EdmType
202 return this._edmType;
206 if (value != null && !Helper.IsScalarType(value))
208 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EntityParameterEdmTypeNotScalar(value.FullName));
211 this._edmType = value;
216 /// The precision of the parameter if the parameter is a floating point type
218 public new byte Precision
222 byte result = this._precision.HasValue ? this._precision.Value : (byte)0;
228 this._precision = value;
233 /// The scale of the parameter if the parameter is a floating point type
235 public new byte Scale
239 byte result = this._scale.HasValue ? this._scale.Value : (byte)0;
250 /// The value of the parameter
252 public override object Value
260 // If the user hasn't set the DbType, then we have to figure out if the DbType will change as a result
261 // of the change in the value. What we want to achieve is that changes to the value will not cause
262 // it to be dirty, but changes to the value that causes the apparent DbType to change, then should be
264 if (!this._dbType.HasValue && this._edmType == null)
266 // If the value is null, then we assume it's string type
267 DbType oldDbType = DbType.String;
270 oldDbType = TypeHelpers.ConvertClrTypeToDbType(_value.GetType());
273 // If the value is null, then we assume it's string type
274 DbType newDbType = DbType.String;
277 newDbType = TypeHelpers.ConvertClrTypeToDbType(value.GetType());
280 if (oldDbType != newDbType)
291 /// Gets whether this collection has been changes since the last reset
293 internal bool IsDirty
302 /// Indicates whether the DbType property has been set by the user;
304 internal bool IsDbTypeSpecified
308 return this._dbType.HasValue;
313 /// Indicates whether the Direction property has been set by the user;
315 internal bool IsDirectionSpecified
319 return this._direction != 0;
324 /// Indicates whether the IsNullable property has been set by the user;
326 internal bool IsIsNullableSpecified
330 return this._isNullable.HasValue;
335 /// Indicates whether the Precision property has been set by the user;
337 internal bool IsPrecisionSpecified
341 return this._precision.HasValue;
346 /// Indicates whether the Scale property has been set by the user;
348 internal bool IsScaleSpecified
352 return this._scale.HasValue;
357 /// Indicates whether the Size property has been set by the user;
359 internal bool IsSizeSpecified
363 return this._size.HasValue;
368 /// Resets the DbType property to its original settings
370 public override void ResetDbType()
372 if (_dbType != null || _edmType != null)
382 /// Clones this parameter object
384 /// <returns>The new cloned object</returns>
385 internal EntityParameter Clone()
387 return new EntityParameter(this);
391 /// Clones this parameter object
393 /// <returns>The new cloned object</returns>
394 private void CloneHelper(EntityParameter destination)
396 CloneHelperCore(destination);
398 destination._parameterName = _parameterName;
399 destination._dbType = _dbType;
400 destination._edmType = _edmType;
401 destination._precision = _precision;
402 destination._scale = _scale;
406 /// Marks that this parameter has been changed
408 private void PropertyChanging()
414 /// Determines the size of the given object
416 /// <param name="value"></param>
417 /// <returns></returns>
418 private int ValueSize(object value)
420 return ValueSizeCore(value);
424 /// Get the type usage for this parameter in model terms.
426 /// <returns>The type usage for this parameter</returns>
427 //NOTE: Because GetTypeUsage throws CommandValidationExceptions, it should only be called from EntityCommand during command execution
428 internal TypeUsage GetTypeUsage()
431 if (!this.IsTypeConsistent)
433 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EntityParameterInconsistentEdmType(
434 _edmType.FullName, _parameterName));
437 if (this._edmType != null)
439 typeUsage = TypeUsage.Create(this._edmType);
441 else if (!DbTypeMap.TryGetModelTypeUsage(this.DbType, out typeUsage))
443 // Spatial types have only DbType 'Object', and cannot be represented in the static type map.
444 PrimitiveType primitiveParameterType;
445 if (this.DbType == DbType.Object &&
446 this.Value != null &&
447 ClrProviderManifest.Instance.TryGetPrimitiveType(this.Value.GetType(), out primitiveParameterType) &&
448 Helper.IsSpatialType(primitiveParameterType))
450 typeUsage = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveParameterType.PrimitiveTypeKind);
454 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnsupportedDbType(this.DbType.ToString(), ParameterName));
458 Debug.Assert(typeUsage != null, "DbType.TryGetModelTypeUsage returned true for null TypeUsage?");
463 /// Reset the dirty flag on the collection
465 internal void ResetIsDirty()
470 private bool IsTypeConsistent
474 if (this._edmType != null && this._dbType.HasValue)
476 DbType dbType = GetDbTypeFromEdm(_edmType);
477 if (dbType == DbType.String)
479 // would need facets to distinguish the various sorts of string,
480 // a generic string EdmType is consistent with any string DbType.
481 return _dbType == DbType.String || _dbType == DbType.AnsiString
482 || dbType == DbType.AnsiStringFixedLength || dbType == DbType.StringFixedLength;
486 return _dbType == dbType;
494 private static DbType GetDbTypeFromEdm(EdmType edmType)
496 PrimitiveType primitiveType = Helper.AsPrimitive(edmType);
498 if (Helper.IsSpatialType(primitiveType))
500 return DbType.Object;
502 else if (DbCommandDefinition.TryGetDbTypeFromPrimitiveType(primitiveType, out dbType))
507 // we shouldn't ever get here. Assert in a debug build, and pick a type.
508 Debug.Assert(false, "The provided edmType is of an unknown primitive type.");
509 return default(DbType);