0
|
1 using System;
|
|
2 using System.Collections.Generic;
|
|
3 using System.Data;
|
|
4 using System.Data.Common;
|
|
5 using System.Data.Linq;
|
|
6 using System.Linq;
|
|
7
|
|
8 namespace BLToolkit.Data.DataProvider
|
|
9 {
|
|
10 using Common;
|
|
11 using Mapping;
|
|
12 using Sql.SqlProvider;
|
|
13
|
|
14 /// <summary>
|
|
15 /// The <b>DataProviderBase</b> is a class that provides specific data provider information
|
|
16 /// for the <see cref="DbManager"/> class.
|
|
17 /// </summary>
|
|
18 /// <remarks>
|
|
19 /// See the <see cref="DbManager.AddDataProvider(DataProviderBase)"/> method to find an example.
|
|
20 /// </remarks>
|
|
21 /// <seealso cref="DbManager.AddDataProvider(DataProviderBase)">AddDataManager Method</seealso>
|
|
22 public abstract partial class DataProviderBase : IMappingSchemaProvider
|
|
23 {
|
|
24 #region Abstract Properties
|
|
25
|
|
26 /// <summary>
|
|
27 /// Returns an actual type of the connection object used by this instance of the <see cref="DbManager"/>.
|
|
28 /// </summary>
|
|
29 /// <remarks>
|
|
30 /// See the <see cref="DbManager.AddDataProvider(DataProviderBase)"/> method to find an example.
|
|
31 /// </remarks>
|
|
32 /// <seealso cref="DbManager.AddDataProvider(DataProviderBase)">AddDataManager Method</seealso>
|
|
33 /// <value>An instance of the <see cref="Type"/> class.</value>
|
|
34 public abstract Type ConnectionType { get; }
|
|
35
|
|
36 /// <summary>
|
|
37 /// Returns the data manager name.
|
|
38 /// </summary>
|
|
39 /// <remarks>
|
|
40 /// See the <see cref="DbManager.AddDataProvider(DataProviderBase)"/> method to find an example.
|
|
41 /// </remarks>
|
|
42 /// <seealso cref="DbManager.AddDataProvider(DataProviderBase)">AddDataManager Method</seealso>
|
|
43 /// <value>The data manager name.</value>
|
|
44 public abstract string Name { get; }
|
|
45
|
|
46 private string _uniqueName;
|
|
47 /// <summary>
|
|
48 /// Same as <see cref="Name"/>, but may be overridden to add two or more providers of same type.
|
|
49 /// </summary>
|
|
50 public string UniqueName
|
|
51 {
|
|
52 get { return _uniqueName ?? Name; }
|
|
53 internal set { _uniqueName = value; }
|
|
54 }
|
|
55
|
|
56 #endregion
|
|
57
|
|
58 #region Abstract Methods
|
|
59
|
|
60 /// <summary>
|
|
61 /// Creates a new instance of the <see cref="IDbConnection"/>.
|
|
62 /// </summary>
|
|
63 /// <remarks>
|
|
64 /// See the <see cref="DbManager.AddDataProvider(DataProviderBase)"/> method to find an example.
|
|
65 /// </remarks>
|
|
66 /// <seealso cref="DbManager.AddDataProvider(DataProviderBase)">AddDataManager Method</seealso>
|
|
67 /// <returns>The <see cref="IDbConnection"/> object.</returns>
|
|
68 public abstract IDbConnection CreateConnectionObject();
|
|
69
|
|
70 /// <summary>
|
|
71 /// Creates a new connection object with same connection string.
|
|
72 /// </summary>
|
|
73 /// <param name="connection">A connection object used as prototype.</param>
|
|
74 /// <returns>New connection instance.</returns>
|
|
75 public virtual IDbConnection CloneConnection(IDbConnection connection)
|
|
76 {
|
|
77 if (connection == null)
|
|
78 throw new ArgumentNullException("connection");
|
|
79
|
|
80 var cloneable = connection as ICloneable;
|
|
81
|
|
82 if (cloneable != null)
|
|
83 return (IDbConnection)cloneable.Clone();
|
|
84
|
|
85 var newConnection = CreateConnectionObject();
|
|
86
|
|
87 // This is definitelly not enought when PersistSecurityInfo set to false.
|
|
88 //
|
|
89 newConnection.ConnectionString = connection.ConnectionString;
|
|
90
|
|
91 return newConnection;
|
|
92 }
|
|
93
|
|
94 /// <summary>
|
|
95 /// Creates an instance of the <see cref="DbDataAdapter"/>.
|
|
96 /// </summary>
|
|
97 /// <remarks>
|
|
98 /// See the <see cref="DbManager.AddDataProvider(DataProviderBase)"/> method to find an example.
|
|
99 /// </remarks>
|
|
100 /// <seealso cref="DbManager.AddDataProvider(DataProviderBase)">AddDataManager Method</seealso>
|
|
101 /// <returns>The <see cref="DbDataAdapter"/> object.</returns>
|
|
102 public abstract DbDataAdapter CreateDataAdapterObject();
|
|
103
|
|
104 /// <summary>
|
|
105 /// Populates the specified <see cref="IDbCommand"/> object's Parameters collection with
|
|
106 /// parameter information for the stored procedure specified in the <see cref="IDbCommand"/>.
|
|
107 /// </summary>
|
|
108 /// <remarks>
|
|
109 /// See the <see cref="DbManager.AddDataProvider(DataProviderBase)"/> method to find an example.
|
|
110 /// </remarks>
|
|
111 /// <seealso cref="DbManager.AddDataProvider(DataProviderBase)">AddDataManager Method</seealso>
|
|
112 /// <param name="command">The <see cref="IDbCommand"/> referencing the stored procedure
|
|
113 /// for which the parameter information is to be derived.
|
|
114 /// The derived parameters will be populated into the Parameters of this command.</param>
|
|
115 /// <returns>true - parameters can be derive.</returns>
|
|
116 public abstract bool DeriveParameters(IDbCommand command);
|
|
117
|
|
118 #endregion
|
|
119
|
|
120 #region Factory methods
|
|
121
|
|
122 public virtual IDbCommand CreateCommandObject(IDbConnection connection)
|
|
123 {
|
|
124 return connection.CreateCommand();
|
|
125 }
|
|
126
|
|
127 public virtual IDbDataParameter CreateParameterObject(IDbCommand command)
|
|
128 {
|
|
129 return command.CreateParameter();
|
|
130 }
|
|
131
|
|
132 #endregion
|
|
133
|
|
134 #region IDbDataParameter methods
|
|
135
|
|
136 public virtual IDbDataParameter GetParameter(
|
|
137 IDbCommand command,
|
|
138 NameOrIndexParameter nameOrIndex)
|
|
139 {
|
|
140 return (IDbDataParameter)(nameOrIndex.ByName ?
|
|
141 command.Parameters[nameOrIndex.Name] : command.Parameters[nameOrIndex.Index]);
|
|
142 }
|
|
143
|
|
144 public virtual void AttachParameter(
|
|
145 IDbCommand command,
|
|
146 IDbDataParameter parameter)
|
|
147 {
|
|
148 command.Parameters.Add(parameter);
|
|
149 }
|
|
150
|
|
151 public virtual void SetUserDefinedType(IDbDataParameter parameter, string typeName)
|
|
152 {
|
|
153 throw new NotSupportedException(Name + " data provider does not support UDT.");
|
|
154 }
|
|
155
|
|
156 public virtual bool IsValueParameter(IDbDataParameter parameter)
|
|
157 {
|
|
158 return parameter.Direction != ParameterDirection.ReturnValue;
|
|
159 }
|
|
160
|
|
161 public virtual IDbDataParameter CloneParameter(IDbDataParameter parameter)
|
|
162 {
|
|
163 return (IDbDataParameter)((ICloneable)parameter).Clone();
|
|
164 }
|
|
165
|
|
166 public virtual bool InitParameter(IDbDataParameter parameter)
|
|
167 {
|
|
168 return false;
|
|
169 }
|
|
170
|
|
171 #endregion
|
|
172
|
|
173 #region Virtual Members
|
|
174
|
|
175 /// <summary>
|
|
176 /// Open an <see cref="IDataReader"/> into the given RefCursor object
|
|
177 /// </summary>
|
|
178 /// <param name="refCursor">The refcursor to open an <see cref="IDataReader"/> to</param>
|
|
179 /// <returns>The <see cref="IDataReader"/> into the refcursor</returns>
|
|
180 public virtual IDataReader GetRefCursorDataReader(object refCursor)
|
|
181 {
|
|
182 throw new NotSupportedException("Operation not supported on this DataProvider");
|
|
183 }
|
|
184
|
|
185 public virtual object Convert(object value, ConvertType convertType)
|
|
186 {
|
|
187 return SqlProvider.Convert(value, convertType);
|
|
188 }
|
|
189
|
|
190 public virtual DataExceptionType ConvertErrorNumberToDataExceptionType(int number)
|
|
191 {
|
|
192 return DataExceptionType.Undefined;
|
|
193 }
|
|
194
|
|
195 public virtual void InitDbManager(DbManager dbManager)
|
|
196 {
|
|
197 var schema = MappingSchema;
|
|
198
|
|
199 if (schema != null)
|
|
200 dbManager.MappingSchema = schema;
|
|
201 }
|
|
202
|
|
203 /// <summary>
|
|
204 /// One time initialization from a configuration file.
|
|
205 /// </summary>
|
|
206 /// <param name="attributes">Provider specific attributes.</param>
|
|
207 public virtual void Configure(System.Collections.Specialized.NameValueCollection attributes)
|
|
208 {
|
|
209 }
|
|
210
|
|
211 public virtual MappingSchema MappingSchema { get; set; }
|
|
212
|
|
213 public virtual void PrepareCommand(ref CommandType commandType, ref string commandText, ref IDbDataParameter[] commandParameters)
|
|
214 {
|
|
215 /*
|
|
216 if (commandParameters != null) foreach (var p in commandParameters)
|
|
217 {
|
|
218 if (p.Value is System.Data.Linq.Binary)
|
|
219 {
|
|
220 var arr = ((System.Data.Linq.Binary)p.Value).ToArray();
|
|
221
|
|
222 p.Value = arr;
|
|
223 p.DbType = DbType.Binary;
|
|
224 p.Size = arr.Length;
|
|
225 }
|
|
226 }
|
|
227 */
|
|
228 }
|
|
229
|
|
230 public virtual bool CanReuseCommand(IDbCommand command, CommandType newCommandType)
|
|
231 {
|
|
232 return true;
|
|
233 }
|
|
234
|
|
235 public virtual int ExecuteArray(IDbCommand command, int iterations)
|
|
236 {
|
|
237 // save parameter values
|
|
238 var parameters = command.Parameters
|
|
239 .OfType<IDbDataParameter>()
|
|
240 .Select(param => new
|
|
241 {
|
|
242 Parameter = param,
|
|
243 Value = param.Value as Array
|
|
244 })
|
|
245 .ToArray();
|
|
246
|
|
247 var outParameters = parameters
|
|
248 .Where(p =>
|
|
249 p.Parameter.Direction == ParameterDirection.InputOutput ||
|
|
250 p.Parameter.Direction == ParameterDirection.Output)
|
|
251 .ToArray();
|
|
252
|
|
253 // validate parameter values
|
|
254 foreach (var p in parameters)
|
|
255 {
|
|
256 if (p.Value == null)
|
|
257 {
|
|
258 throw new InvalidOperationException("ExecuteArray requires that all " +
|
|
259 "parameter values are arrays. Parameter name: " + p.Parameter.ParameterName);
|
|
260 }
|
|
261
|
|
262 if (p.Value.GetLength(0) != iterations)
|
|
263 {
|
|
264 throw new InvalidOperationException("ExecuteArray requires that array sizes are " +
|
|
265 "equal to the number of iterations. Parameter name: " + p.Parameter.ParameterName);
|
|
266 }
|
|
267 }
|
|
268
|
|
269 try
|
|
270 {
|
|
271 // run iterations
|
|
272 int rowsAffected = 0;
|
|
273 for (int iteration = 0; iteration < iterations; iteration++)
|
|
274 {
|
|
275 // copy input parameter values
|
|
276 foreach (var param in parameters)
|
|
277 {
|
|
278 SetParameterValue(param.Parameter, param.Value.GetValue(iteration));
|
|
279 }
|
|
280
|
|
281 rowsAffected += command.ExecuteNonQuery();
|
|
282
|
|
283 // return output parameter values
|
|
284 foreach (var param in outParameters)
|
|
285 {
|
|
286 var outputValue = param.Parameter.Value;
|
|
287 param.Value.SetValue(outputValue, iteration);
|
|
288 }
|
|
289 }
|
|
290
|
|
291 return rowsAffected;
|
|
292 }
|
|
293 finally
|
|
294 {
|
|
295 // restore parameter values
|
|
296 foreach (var param in parameters)
|
|
297 {
|
|
298 param.Parameter.Value = param.Value;
|
|
299 }
|
|
300 }
|
|
301 }
|
|
302
|
|
303 public virtual string GetSequenceQuery(string sequenceName)
|
|
304 {
|
|
305 return null;
|
|
306 }
|
|
307
|
|
308 public virtual string NextSequenceQuery(string sequenceName)
|
|
309 {
|
|
310 return null;
|
|
311 }
|
|
312
|
|
313 public virtual string GetReturningInto(string columnName)
|
|
314 {
|
|
315 return null;
|
|
316 }
|
|
317
|
|
318 public virtual void SetParameterValue(IDbDataParameter parameter, object value)
|
|
319 {
|
|
320 if (value is System.Data.Linq.Binary)
|
|
321 {
|
|
322 var arr = ((System.Data.Linq.Binary)value).ToArray();
|
|
323
|
|
324 parameter.Value = arr;
|
|
325 parameter.DbType = DbType.Binary;
|
|
326 parameter.Size = arr.Length;
|
|
327 }
|
|
328 else
|
|
329 parameter.Value = value;
|
|
330 }
|
|
331
|
|
332 public abstract ISqlProvider CreateSqlProvider();
|
|
333
|
|
334 private ISqlProvider _sqlProvider;
|
|
335 protected ISqlProvider SqlProvider
|
|
336 {
|
|
337 get { return _sqlProvider ?? (_sqlProvider = CreateSqlProvider()); }
|
|
338 }
|
|
339
|
|
340 public virtual IDataReader GetDataReader(MappingSchema schema, IDataReader dataReader)
|
|
341 {
|
|
342 return dataReader;
|
|
343 }
|
|
344
|
|
345 public virtual IDataReader GetDataReader(IDbCommand command, CommandBehavior commandBehavior)
|
|
346 {
|
|
347 return command.ExecuteReader(commandBehavior);
|
|
348 }
|
|
349
|
|
350 public virtual bool ParameterNamesEqual(string paramName1, string paramName2)
|
|
351 {
|
|
352 // default implementation is case-insensitive, because if we make it
|
|
353 // case-sensitive and don't overload it in all existing providers - client code may break
|
|
354 return string.Equals(paramName1, paramName2, StringComparison.OrdinalIgnoreCase);
|
|
355 }
|
|
356
|
|
357 public virtual DbType GetDbType(Type systemType)
|
|
358 {
|
|
359 if (systemType == typeof(Binary) || systemType == typeof(byte[]))
|
|
360 return DbType.Binary;
|
|
361
|
|
362 return DbType.Object;
|
|
363 }
|
|
364
|
|
365 public virtual bool IsMarsEnabled(IDbConnection conn)
|
|
366 {
|
|
367 return false;
|
|
368 }
|
|
369
|
|
370 public virtual string ProviderName { get { return ConnectionType.Namespace; } }
|
|
371 public virtual int MaxParameters { get { return 100; } }
|
|
372 public virtual int MaxBatchSize { get { return 65536; } }
|
|
373 public virtual string EndOfSql { get { return ";"; } }
|
|
374
|
|
375 #endregion
|
|
376
|
|
377 #region DataReaderEx
|
|
378
|
|
379 protected class DataReaderBase<T> : IDataReader
|
|
380 where T: IDataReader
|
|
381 {
|
|
382 public readonly T DataReader;
|
|
383
|
|
384 protected DataReaderBase(T rd)
|
|
385 {
|
|
386 DataReader = rd;
|
|
387 }
|
|
388
|
|
389 #region Implementation of IDisposable
|
|
390
|
|
391 public void Dispose()
|
|
392 {
|
|
393 DataReader.Dispose();
|
|
394 }
|
|
395
|
|
396 #endregion
|
|
397
|
|
398 #region Implementation of IDataRecord
|
|
399
|
|
400 public string GetName (int i) { return DataReader.GetName (i); }
|
|
401 public string GetDataTypeName(int i) { return DataReader.GetDataTypeName(i); }
|
|
402 public Type GetFieldType (int i) { return DataReader.GetFieldType (i); }
|
|
403 // GetValue method is virtual since it can be overridden by some data provider
|
|
404 // (For instance, OdbDataProvider uses special methodes for clob data fetching)
|
|
405 public virtual object GetValue (int i) { return DataReader.GetValue (i); }
|
|
406 public int GetValues (object[] values) { return DataReader.GetValues (values); }
|
|
407 public int GetOrdinal (string name) { return DataReader.GetOrdinal (name); }
|
|
408 public bool GetBoolean (int i) { return DataReader.GetBoolean (i); }
|
|
409 public byte GetByte (int i) { return DataReader.GetByte (i); }
|
|
410 public char GetChar (int i) { return DataReader.GetChar (i); }
|
|
411 public Guid GetGuid (int i) { return DataReader.GetGuid (i); }
|
|
412 public short GetInt16 (int i) { return DataReader.GetInt16 (i); }
|
|
413 public int GetInt32 (int i) { return DataReader.GetInt32 (i); }
|
|
414 public long GetInt64 (int i) { return DataReader.GetInt64 (i); }
|
|
415 public float GetFloat (int i) { return DataReader.GetFloat (i); }
|
|
416 public double GetDouble (int i) { return DataReader.GetDouble (i); }
|
|
417 public string GetString (int i) { return DataReader.GetString (i); }
|
|
418 public decimal GetDecimal (int i) { return DataReader.GetDecimal (i); }
|
|
419 public DateTime GetDateTime (int i) { return DataReader.GetDateTime (i); }
|
|
420 public IDataReader GetData (int i) { return DataReader.GetData (i); }
|
|
421 public bool IsDBNull (int i) { return DataReader.IsDBNull (i); }
|
|
422
|
|
423 public int FieldCount { get { return DataReader.FieldCount; } }
|
|
424
|
|
425 object IDataRecord.this[int i] { get { return DataReader[i]; } }
|
|
426 object IDataRecord.this[string name] { get { return DataReader[name]; } }
|
|
427
|
|
428 public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
|
|
429 {
|
|
430 return DataReader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
|
|
431 }
|
|
432
|
|
433 public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
|
|
434 {
|
|
435 return DataReader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
|
|
436 }
|
|
437
|
|
438 #endregion
|
|
439
|
|
440 #region Implementation of IDataReader
|
|
441
|
|
442 public void Close () { DataReader.Close (); }
|
|
443 public DataTable GetSchemaTable() { return DataReader.GetSchemaTable(); }
|
|
444 public bool NextResult () { return DataReader.NextResult (); }
|
|
445 public bool Read () { return DataReader.Read (); }
|
|
446 public int Depth { get { return DataReader.Depth; } }
|
|
447 public bool IsClosed { get { return DataReader.IsClosed; } }
|
|
448 public int RecordsAffected { get { return DataReader.RecordsAffected; } }
|
|
449
|
|
450 #endregion
|
|
451 }
|
|
452
|
|
453 protected abstract class DataReaderEx<T> : DataReaderBase<T>, IDataReaderEx
|
|
454 where T: IDataReader
|
|
455 {
|
|
456 protected DataReaderEx(T rd) : base(rd)
|
|
457 {
|
|
458 }
|
|
459
|
|
460 #region Implementation of IDataReaderEx
|
|
461
|
|
462 public abstract DateTimeOffset GetDateTimeOffset(int i);
|
|
463
|
|
464 #endregion
|
|
465 }
|
|
466
|
|
467 #endregion
|
|
468
|
|
469 #region InsertBatch
|
|
470
|
|
471 public virtual int InsertBatchWithIdentity<T>(
|
|
472 DbManager db,
|
|
473 string insertText,
|
|
474 IEnumerable<T> collection,
|
|
475 MemberMapper[] members,
|
|
476 int maxBatchSize,
|
|
477 DbManager.ParameterProvider<T> getParameters)
|
|
478 {
|
|
479 throw new NotImplementedException("Insert batch with identity is not implemented!");
|
|
480 }
|
|
481
|
|
482 public virtual int InsertBatch<T>(
|
|
483 DbManager db,
|
|
484 string insertText,
|
|
485 IEnumerable<T> collection,
|
|
486 MemberMapper[] members,
|
|
487 int maxBatchSize,
|
|
488 DbManager.ParameterProvider<T> getParameters)
|
|
489 {
|
|
490 db.SetCommand(insertText);
|
|
491 return db.ExecuteForEach(collection, members, maxBatchSize, getParameters);
|
|
492 }
|
|
493
|
|
494 #endregion
|
|
495
|
|
496 protected int ExecuteSqlList(DbManager db, IEnumerable<string> sqlList)
|
|
497 {
|
|
498 var cnt = 0;
|
|
499
|
|
500 foreach (string sql in sqlList)
|
|
501 {
|
|
502 cnt += db.SetCommand(sql).ExecuteNonQuery();
|
|
503 }
|
|
504
|
|
505 return cnt;
|
|
506 }
|
|
507
|
|
508 public virtual DbType GetParameterDbType(DbType dbType)
|
|
509 {
|
|
510 return dbType;
|
|
511 }
|
|
512 }
|
|
513 }
|