ed4b28e278fb2753ef394e71a1fd0617d22207e0
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / DbCommandDefinition.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DbCommandDefinition.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.Diagnostics;
11 using System.Data.Metadata.Edm;
12
13 namespace System.Data.Common {
14
15     /// <summary>
16     /// A prepared command definition, can be cached and reused to avoid 
17     /// repreparing a command.
18     /// </summary>
19     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")]
20     public class DbCommandDefinition {
21
22         private readonly ICloneable _prototype;
23
24         /// <summary>
25         /// Internal factory method to create the default Command Definition object
26         /// based on a prototype command. The prototype command is cloned 
27         /// before the protected constructor is invoked
28         /// </summary>
29         /// <param name="prototype">prototype DbCommand</param>
30         /// <returns>the DbCommandDefinition</returns>
31         internal static DbCommandDefinition CreateCommandDefinition(DbCommand prototype) {
32             EntityUtil.CheckArgumentNull(prototype, "prototype");
33             ICloneable cloneablePrototype = prototype as ICloneable;
34             if (null == cloneablePrototype) {
35                 throw EntityUtil.CannotCloneStoreProvider();
36             }
37             DbCommand clonedPrototype = (DbCommand)(cloneablePrototype.Clone());
38             return new DbCommandDefinition(clonedPrototype);
39         }
40
41         /// <summary>
42         /// Protected constructor; the command is assumed to be a prototype
43         /// that will be cloned on CreateCommand, and the cloned command will be executed.
44         /// </summary>
45         protected DbCommandDefinition(DbCommand prototype) {
46             EntityUtil.CheckArgumentNull(prototype, "prototype");
47             _prototype = prototype as ICloneable;
48             if (null == _prototype) {
49                 throw EntityUtil.CannotCloneStoreProvider();
50             }
51         }
52
53         /// <summary>
54         /// Constructor overload for subclasses to use
55         /// </summary>
56         protected DbCommandDefinition() {
57         }
58
59         /// <summary>
60         /// Create a DbCommand object from the definition, that can be executed.
61         /// </summary>
62         /// <returns></returns>
63         public virtual DbCommand CreateCommand() {
64             return (DbCommand)(_prototype.Clone());
65         }
66
67         internal static void PopulateParameterFromTypeUsage(DbParameter parameter, TypeUsage type, bool isOutParam)
68         {
69             EntityUtil.CheckArgumentNull(parameter, "parameter");
70             EntityUtil.CheckArgumentNull(type, "type");
71             
72             // parameter.IsNullable - from the NullableConstraintAttribute value
73             parameter.IsNullable = TypeSemantics.IsNullable(type);
74             
75             // parameter.ParameterName - set by the caller;
76             // parameter.SourceColumn - not applicable until we have a data adapter;
77             // parameter.SourceColumnNullMapping - not applicable until we have a data adapter;
78             // parameter.SourceVersion - not applicable until we have a data adapter;
79             // parameter.Value - left unset;
80             // parameter.DbType - determined by the TypeMapping;
81             // parameter.Precision - from the TypeMapping;
82             // parameter.Scale - from the TypeMapping;
83             // parameter.Size - from the TypeMapping;
84
85
86             // type.EdmType may not be a primitive type here - e.g. the user specified
87             // a complex or entity type when creating an ObjectParameter instance. To keep 
88             // the same behavior we had in previous versions we let it through here. We will 
89             // throw an exception later when actually invoking the stored procedure where we
90             // don't allow parameters that are non-primitive.
91             if(Helper.IsPrimitiveType(type.EdmType))
92             {
93                 DbType dbType;
94                 if (TryGetDbTypeFromPrimitiveType((PrimitiveType)type.EdmType, out dbType))
95                 {
96                     switch (dbType)
97                     {
98                         case DbType.Binary:
99                             PopulateBinaryParameter(parameter, type, dbType, isOutParam);
100                             break;
101                         case DbType.DateTime:
102                         case DbType.Time:
103                         case DbType.DateTimeOffset:
104                             PopulateDateTimeParameter(parameter, type, dbType);
105                             break;
106                         case DbType.Decimal:
107                             PopulateDecimalParameter(parameter, type, dbType);
108                             break;
109                         case DbType.String:
110                             PopulateStringParameter(parameter, type, isOutParam);
111                             break;
112                         default:
113                             parameter.DbType = dbType;
114                             break;
115                     }
116                 }
117             }
118         }
119
120         internal static bool TryGetDbTypeFromPrimitiveType(PrimitiveType type, out DbType dbType)
121         {
122             switch (type.PrimitiveTypeKind)
123             {
124                 case PrimitiveTypeKind.Binary:
125                     dbType = DbType.Binary; 
126                     return true;
127                 case PrimitiveTypeKind.Boolean:
128                     dbType = DbType.Boolean; 
129                     return true;
130                 case PrimitiveTypeKind.Byte:
131                     dbType = DbType.Byte; 
132                     return true;
133                 case PrimitiveTypeKind.DateTime:
134                     dbType = DbType.DateTime; 
135                     return true;
136                 case PrimitiveTypeKind.Time:
137                     dbType = DbType.Time; 
138                     return true;
139                 case PrimitiveTypeKind.DateTimeOffset:
140                     dbType = DbType.DateTimeOffset; 
141                     return true;
142                 case PrimitiveTypeKind.Decimal:
143                     dbType = DbType.Decimal; 
144                     return true;
145                 case PrimitiveTypeKind.Double:
146                     dbType = DbType.Double; 
147                     return true;
148                 case PrimitiveTypeKind.Guid:
149                     dbType = DbType.Guid; 
150                     return true;
151                 case PrimitiveTypeKind.Single:
152                     dbType = DbType.Single; 
153                     return true;
154                 case PrimitiveTypeKind.SByte:
155                     dbType = DbType.SByte; 
156                     return true;
157                 case PrimitiveTypeKind.Int16:
158                     dbType = DbType.Int16; 
159                     return true;
160                 case PrimitiveTypeKind.Int32:
161                     dbType = DbType.Int32; 
162                     return true;
163                 case PrimitiveTypeKind.Int64:
164                     dbType = DbType.Int64; 
165                     return true;
166                 case PrimitiveTypeKind.String:
167                     dbType = DbType.String; 
168                     return true;
169                 default:
170                     dbType = default(DbType);
171                     return  false;
172             }
173         }
174
175         private static void PopulateBinaryParameter(DbParameter parameter, TypeUsage type, DbType dbType, bool isOutParam)
176         {
177             parameter.DbType = dbType;
178
179             // For each facet, set the facet value only if we have it, note that it's possible to not have
180             // it in the case the facet value is null
181             SetParameterSize(parameter, type, isOutParam);
182         }
183
184         private static void PopulateDecimalParameter (DbParameter parameter, TypeUsage type, DbType dbType)
185         {
186             parameter.DbType = dbType;
187             IDbDataParameter dataParameter = (IDbDataParameter)parameter;
188
189             // For each facet, set the facet value only if we have it, note that it's possible to not have
190             // it in the case the facet value is null
191             byte precision;
192             byte scale;
193             if (TypeHelpers.TryGetPrecision(type, out precision))
194             {
195                 dataParameter.Precision = precision;
196             }
197
198             if (TypeHelpers.TryGetScale(type, out scale))
199             {
200                 dataParameter.Scale = scale;
201             }
202         }
203
204         private static void PopulateDateTimeParameter(DbParameter parameter, TypeUsage type, DbType dbType)
205         {
206             parameter.DbType = dbType;
207             IDbDataParameter dataParameter = (IDbDataParameter)parameter;
208
209             // For each facet, set the facet value only if we have it, note that it's possible to not have
210             // it in the case the facet value is null
211             byte precision;
212             if (TypeHelpers.TryGetPrecision(type, out precision))
213             {
214                 dataParameter.Precision = precision;
215             }
216         }
217
218
219         private static void PopulateStringParameter(DbParameter parameter, TypeUsage type, bool isOutParam)
220         {
221             // For each facet, set the facet value only if we have it, note that it's possible to not have
222             // it in the case the facet value is null
223             bool unicode = true;
224             bool fixedLength = false;
225
226             if (!TypeHelpers.TryGetIsFixedLength(type, out fixedLength))
227             {
228                 // If we can't get the fixed length facet value, then default to fixed length = false
229                 fixedLength = false;
230             }
231
232             if (!TypeHelpers.TryGetIsUnicode(type, out unicode))
233             {
234                 // If we can't get the unicode facet value, then default to unicode = true
235                 unicode = true;
236             }
237
238             if (fixedLength)
239             {
240                 parameter.DbType = (unicode ? DbType.StringFixedLength : DbType.AnsiStringFixedLength);
241             }
242             else
243             {
244                 parameter.DbType = (unicode ? DbType.String : DbType.AnsiString);
245             }
246
247             SetParameterSize(parameter, type, isOutParam);
248         }
249
250         private static void SetParameterSize(DbParameter parameter, TypeUsage type, bool isOutParam)
251         {
252             // only set the size if the parameter has a specific size value.
253             Facet maxLengthFacet;
254             if (type.Facets.TryGetValue(DbProviderManifest.MaxLengthFacetName, true, out maxLengthFacet) && maxLengthFacet.Value != null)
255             {
256                 // only set size if there is a specific size
257                 if (!Helper.IsUnboundedFacetValue(maxLengthFacet))
258                 {
259                     parameter.Size = (int)maxLengthFacet.Value;
260                 }
261                 else if (isOutParam)
262                 {
263                     // if it is store procedure parameter and it is unbounded set the size to max
264                     parameter.Size = Int32.MaxValue;
265                 }
266             }
267         }
268     }
269 }