Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityClient / EntityParameter.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityParameter.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 namespace System.Data.EntityClient
10 {
11     using System.Data;
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;
17
18     /// <summary>
19     /// Class representing a parameter used in EntityCommand
20     /// </summary>
21     public sealed partial class EntityParameter : DbParameter, IDbDataParameter
22     {
23         private string _parameterName;
24         private DbType? _dbType;
25         private EdmType _edmType;
26         private byte? _precision;
27         private byte? _scale;
28         private bool _isDirty;
29
30         /// <summary>
31         /// Constructs the EntityParameter object
32         /// </summary>
33         public EntityParameter()
34         {
35         }
36
37         /// <summary>
38         /// Constructs the EntityParameter object with the given parameter name and the type of the parameter
39         /// </summary>
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)
43         {
44             SetParameterNameWithValidation(parameterName, "parameterName");
45             this.DbType = dbType;
46         }
47
48         /// <summary>
49         /// Constructs the EntityParameter object with the given parameter name, the type of the parameter, and the size of the
50         /// parameter
51         /// </summary>
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)
56         {
57             SetParameterNameWithValidation(parameterName, "parameterName");
58             this.DbType = dbType;
59             this.Size = size;
60         }
61
62         /// <summary>
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
65         /// </summary>
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)
71         {
72             SetParameterNameWithValidation(parameterName, "parameterName");
73             this.DbType = dbType;
74             this.Size = size;
75             this.SourceColumn = sourceColumn;
76         }
77
78         /// <summary>
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
81         /// </summary>
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,
93                             DbType dbType,
94                             int size,
95                             ParameterDirection direction,
96                             bool isNullable,
97                             byte precision,
98                             byte scale,
99                             string sourceColumn,
100                             DataRowVersion sourceVersion,
101                             object value)
102         {
103             SetParameterNameWithValidation(parameterName, "parameterName");
104             this.DbType = dbType;
105             this.Size = size;
106             this.Direction = direction;
107             this.IsNullable = isNullable;
108             this.Precision = precision;
109             this.Scale = scale;
110             this.SourceColumn = sourceColumn;
111             this.SourceVersion = sourceVersion;
112             this.Value = value;
113         }
114
115         /// <summary>
116         /// The name of the parameter
117         /// </summary>
118         public override string ParameterName
119         {
120             get
121             {
122                 return this._parameterName ?? "";
123             }
124             set
125             {
126                 SetParameterNameWithValidation(value, "value");
127             }
128         }
129
130         /// <summary>
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.
135         /// </summary>
136         /// <param name="parameterName"></param>
137         /// <param name="argumentName"></param>
138         private void SetParameterNameWithValidation(string parameterName, string argumentName) 
139         {
140             if (!string.IsNullOrEmpty(parameterName) && !DbCommandTree.IsValidParameterName(parameterName)) 
141             {
142                 throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_InvalidParameterName(parameterName), argumentName);
143             }
144             PropertyChanging();
145             this._parameterName = parameterName;
146         }
147
148         /// <summary>
149         /// The type of the parameter, EdmType may also be set, and may provide more detailed information.
150         /// </summary>
151         public override DbType DbType
152         {
153             get
154             {
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)
157                 {
158                     if (this._edmType != null)
159                     {
160                         return GetDbTypeFromEdm(_edmType);
161                     }
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
165                     else
166                     {
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
168                         if (_value == null)
169                             return DbType.String;
170
171                         try
172                         {
173                             return TypeHelpers.ConvertClrTypeToDbType(_value.GetType());
174                         }
175                         catch (ArgumentException e)
176                         {
177                             throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotDeduceDbType, e);
178                         }
179                     }
180                 }
181
182                 return (DbType)this._dbType;
183             }
184             set
185             {
186                 PropertyChanging();
187                 this._dbType = value;
188             }
189         }
190
191         /// <summary>
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).
196         /// </summary>
197         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
198         public EdmType EdmType
199         {
200             get
201             {
202                 return this._edmType;
203             }
204             set
205             {
206                 if (value != null && !Helper.IsScalarType(value))
207                 {
208                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EntityParameterEdmTypeNotScalar(value.FullName));
209                 }
210                 PropertyChanging();
211                 this._edmType = value;
212             }
213         }
214
215         /// <summary>
216         /// The precision of the parameter if the parameter is a floating point type
217         /// </summary>
218         public new byte Precision
219         {
220             get
221             {
222                 byte result = this._precision.HasValue ? this._precision.Value : (byte)0;
223                 return result;
224             }
225             set
226             {
227                 PropertyChanging();
228                 this._precision = value;
229             }
230         }
231
232         /// <summary>
233         /// The scale of the parameter if the parameter is a floating point type
234         /// </summary>
235         public new byte Scale
236         {
237             get
238             {
239                 byte result = this._scale.HasValue ? this._scale.Value : (byte)0;
240                 return result;
241             }
242             set
243             {
244                 PropertyChanging();
245                 this._scale = value;
246             }
247         }
248
249         /// <summary>
250         /// The value of the parameter
251         /// </summary>
252         public override object Value
253         {
254             get
255             {
256                 return this._value;
257             }
258             set
259             {
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
263                 // dirty.
264                 if (!this._dbType.HasValue && this._edmType == null)
265                 {             
266                     // If the value is null, then we assume it's string type
267                     DbType oldDbType = DbType.String;
268                     if (_value != null)
269                     {
270                         oldDbType = TypeHelpers.ConvertClrTypeToDbType(_value.GetType());
271                     }
272
273                     // If the value is null, then we assume it's string type
274                     DbType newDbType = DbType.String;
275                     if (value != null)
276                     {
277                         newDbType = TypeHelpers.ConvertClrTypeToDbType(value.GetType());
278                     }
279
280                     if (oldDbType != newDbType)
281                     {
282                         PropertyChanging();
283                     }
284                 }
285
286                 this._value = value;
287             }
288         }
289
290         /// <summary>
291         /// Gets whether this collection has been changes since the last reset
292         /// </summary>
293         internal bool IsDirty
294         {
295             get
296             {
297                 return _isDirty;
298             }
299         }
300
301         /// <summary>
302         /// Indicates whether the DbType property has been set by the user;
303         /// </summary>
304         internal bool IsDbTypeSpecified
305         {
306             get
307             {
308                 return this._dbType.HasValue;
309             }
310         }
311
312         /// <summary>
313         /// Indicates whether the Direction property has been set by the user;
314         /// </summary>
315         internal bool IsDirectionSpecified
316         {
317             get
318             {
319                 return this._direction != 0;
320             }
321         }
322
323         /// <summary>
324         /// Indicates whether the IsNullable property has been set by the user;
325         /// </summary>
326         internal bool IsIsNullableSpecified
327         {
328             get
329             {
330                 return this._isNullable.HasValue;
331             }
332         }
333
334         /// <summary>
335         /// Indicates whether the Precision property has been set by the user;
336         /// </summary>
337         internal bool IsPrecisionSpecified
338         {
339             get
340             {
341                 return this._precision.HasValue;
342             }
343         }
344
345         /// <summary>
346         /// Indicates whether the Scale property has been set by the user;
347         /// </summary>
348         internal bool IsScaleSpecified
349         {
350             get
351             {
352                 return this._scale.HasValue;
353             }
354         }
355
356         /// <summary>
357         /// Indicates whether the Size property has been set by the user;
358         /// </summary>
359         internal bool IsSizeSpecified
360         {
361             get
362             {
363                 return this._size.HasValue;
364             }
365         }
366
367         /// <summary>
368         /// Resets the DbType property to its original settings
369         /// </summary>
370         public override void ResetDbType()
371         {
372             if (_dbType != null || _edmType != null)
373             {
374                 PropertyChanging();
375             }
376
377             _edmType = null;
378             _dbType = null;
379         }
380
381         /// <summary>
382         /// Clones this parameter object
383         /// </summary>
384         /// <returns>The new cloned object</returns>
385         internal EntityParameter Clone()
386         {
387             return new EntityParameter(this);
388         }
389
390         /// <summary>
391         /// Clones this parameter object
392         /// </summary>
393         /// <returns>The new cloned object</returns>
394         private void CloneHelper(EntityParameter destination)
395         {
396             CloneHelperCore(destination);
397
398             destination._parameterName = _parameterName;
399             destination._dbType = _dbType;
400             destination._edmType = _edmType;
401             destination._precision = _precision;
402             destination._scale = _scale;
403         }
404
405         /// <summary>
406         /// Marks that this parameter has been changed
407         /// </summary>
408         private void PropertyChanging()
409         {
410             _isDirty = true;
411         }
412
413         /// <summary>
414         /// Determines the size of the given object
415         /// </summary>
416         /// <param name="value"></param>
417         /// <returns></returns>
418         private int ValueSize(object value)
419         {
420             return ValueSizeCore(value);
421         }
422
423         /// <summary>
424         /// Get the type usage for this parameter in model terms.
425         /// </summary>
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()
429         {
430             TypeUsage typeUsage;
431             if (!this.IsTypeConsistent)
432             {
433                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EntityParameterInconsistentEdmType(
434                     _edmType.FullName, _parameterName));
435             }
436
437             if (this._edmType != null)
438             {
439                 typeUsage = TypeUsage.Create(this._edmType);
440             }
441             else if (!DbTypeMap.TryGetModelTypeUsage(this.DbType, out typeUsage))
442             {
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))
449                 {
450                     typeUsage = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveParameterType.PrimitiveTypeKind);
451                 }
452                 else
453                 {
454                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnsupportedDbType(this.DbType.ToString(), ParameterName));
455                 }
456             }
457
458             Debug.Assert(typeUsage != null, "DbType.TryGetModelTypeUsage returned true for null TypeUsage?");
459             return typeUsage;
460         }
461
462         /// <summary>
463         /// Reset the dirty flag on the collection
464         /// </summary>
465         internal void ResetIsDirty()
466         {
467             _isDirty = false;
468         }
469
470         private bool IsTypeConsistent
471         {
472             get
473             {
474                 if (this._edmType != null && this._dbType.HasValue)
475                 {
476                     DbType dbType = GetDbTypeFromEdm(_edmType); 
477                     if (dbType == DbType.String)
478                     {
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;
483                     }
484                     else
485                     {
486                         return _dbType == dbType;
487                     }
488                 }
489
490                 return true;
491             }
492         }
493
494         private static DbType GetDbTypeFromEdm(EdmType edmType)
495         {
496             PrimitiveType primitiveType = Helper.AsPrimitive(edmType);
497             DbType dbType;
498             if (Helper.IsSpatialType(primitiveType))
499             {
500                 return DbType.Object;
501             }
502             else if (DbCommandDefinition.TryGetDbTypeFromPrimitiveType(primitiveType, out dbType))
503             {
504                 return dbType;
505             }
506
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);
510         }
511     }
512 }