| 
0
 | 
     1 using System;
 | 
| 
 | 
     2 using System.Data;
 | 
| 
 | 
     3 using System.Data.OleDb;
 | 
| 
 | 
     4 using System.Text.RegularExpressions;
 | 
| 
 | 
     5 
 | 
| 
 | 
     6 namespace BLToolkit.Data.DataProvider
 | 
| 
 | 
     7 {
 | 
| 
 | 
     8 	using Mapping;
 | 
| 
 | 
     9 	using Sql.SqlProvider;
 | 
| 
 | 
    10 
 | 
| 
 | 
    11 	public class AccessDataProvider : OleDbDataProvider
 | 
| 
 | 
    12 	{
 | 
| 
 | 
    13 		private static Regex _paramsExp;
 | 
| 
 | 
    14 
 | 
| 
 | 
    15 		// Based on idea from http://qapi.blogspot.com/2006/12/deriveparameters-oledbprovider-ii.html
 | 
| 
 | 
    16 		//
 | 
| 
 | 
    17 		public override bool DeriveParameters(IDbCommand command)
 | 
| 
 | 
    18 		{
 | 
| 
 | 
    19 			if (command == null)
 | 
| 
 | 
    20 				throw new ArgumentNullException("command");
 | 
| 
 | 
    21 
 | 
| 
 | 
    22 			if (command.CommandType != CommandType.StoredProcedure)
 | 
| 
 | 
    23 				throw new InvalidOperationException("command.CommandType must be CommandType.StoredProcedure");
 | 
| 
 | 
    24 
 | 
| 
 | 
    25 			var conn = command.Connection as OleDbConnection;
 | 
| 
 | 
    26 
 | 
| 
 | 
    27 			if (conn == null || conn.State != ConnectionState.Open)
 | 
| 
 | 
    28 				throw new InvalidOperationException("Invalid connection state.");
 | 
| 
 | 
    29 
 | 
| 
 | 
    30 			command.Parameters.Clear();
 | 
| 
 | 
    31 
 | 
| 
 | 
    32 			var dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Procedures, new object[]{null, null, command.CommandText});
 | 
| 
 | 
    33 
 | 
| 
 | 
    34 			if (dt.Rows.Count == 0)
 | 
| 
 | 
    35 			{
 | 
| 
 | 
    36 				// Jet does convert parameretless procedures to views.
 | 
| 
 | 
    37 				//
 | 
| 
 | 
    38 				dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Views, new object[]{null, null, command.CommandText});
 | 
| 
 | 
    39 
 | 
| 
 | 
    40 				if (dt.Rows.Count == 0)
 | 
| 
 | 
    41 					throw new DataException(string.Format("Stored procedure '{0}' not found", command.CommandText));
 | 
| 
 | 
    42 
 | 
| 
 | 
    43 				// Do nothing. There is no parameters.
 | 
| 
 | 
    44 				//
 | 
| 
 | 
    45 			}
 | 
| 
 | 
    46 			else
 | 
| 
 | 
    47 			{
 | 
| 
 | 
    48 				var col = dt.Columns["PROCEDURE_DEFINITION"];
 | 
| 
 | 
    49 
 | 
| 
 | 
    50 				if (col == null)
 | 
| 
 | 
    51 				{
 | 
| 
 | 
    52 					// Not really possible
 | 
| 
 | 
    53 					//
 | 
| 
 | 
    54 					return false;
 | 
| 
 | 
    55 				}
 | 
| 
 | 
    56 
 | 
| 
 | 
    57 				if (_paramsExp == null)
 | 
| 
 | 
    58 					_paramsExp = new Regex(@"PARAMETERS ((\[(?<name>[^\]]+)\]|(?<name>[^\s]+))\s(?<type>[^,;\s]+(\s\([^\)]+\))?)[,;]\s)*", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
 | 
| 
 | 
    59 
 | 
| 
 | 
    60 				var match = _paramsExp.Match((string)dt.Rows[0][col.Ordinal]);
 | 
| 
 | 
    61 				var names = match.Groups["name"].Captures;
 | 
| 
 | 
    62 				var types = match.Groups["type"].Captures;
 | 
| 
 | 
    63 
 | 
| 
 | 
    64 				if (names.Count != types.Count)
 | 
| 
 | 
    65 				{
 | 
| 
 | 
    66 					// Not really possible
 | 
| 
 | 
    67 					//
 | 
| 
 | 
    68 					return false;
 | 
| 
 | 
    69 				}
 | 
| 
 | 
    70 
 | 
| 
 | 
    71 				var separators = new[] {' ', '(', ',', ')'};
 | 
| 
 | 
    72 
 | 
| 
 | 
    73 				for (var i = 0; i < names.Count; ++i)
 | 
| 
 | 
    74 				{
 | 
| 
 | 
    75 					var paramName = names[i].Value;
 | 
| 
 | 
    76 					var rawType   = types[i].Value.Split(separators, StringSplitOptions.RemoveEmptyEntries);
 | 
| 
 | 
    77 					var p         = new OleDbParameter(paramName, GetOleDbType(rawType[0]));
 | 
| 
 | 
    78 
 | 
| 
 | 
    79 					if (rawType.Length > 2)
 | 
| 
 | 
    80 					{
 | 
| 
 | 
    81 						p.Precision = Common.Convert.ToByte(rawType[1]);
 | 
| 
 | 
    82 						p.Scale     = Common.Convert.ToByte(rawType[2]);
 | 
| 
 | 
    83 					}
 | 
| 
 | 
    84 					else if (rawType.Length > 1)
 | 
| 
 | 
    85 					{
 | 
| 
 | 
    86 						p.Size      = Common.Convert.ToInt32(rawType[1]);
 | 
| 
 | 
    87 					}
 | 
| 
 | 
    88 
 | 
| 
 | 
    89 					command.Parameters.Add(p);
 | 
| 
 | 
    90 				}
 | 
| 
 | 
    91 			}
 | 
| 
 | 
    92 
 | 
| 
 | 
    93 			return true;
 | 
| 
 | 
    94 		}
 | 
| 
 | 
    95 
 | 
| 
 | 
    96 		private static OleDbType GetOleDbType(string jetType)
 | 
| 
 | 
    97 		{
 | 
| 
 | 
    98 			switch (jetType.ToLower())
 | 
| 
 | 
    99 			{
 | 
| 
 | 
   100 				case "byte":
 | 
| 
 | 
   101 				case "tinyint":
 | 
| 
 | 
   102 				case "integer1":
 | 
| 
 | 
   103 					return OleDbType.TinyInt;
 | 
| 
 | 
   104 
 | 
| 
 | 
   105 				case "short":
 | 
| 
 | 
   106 				case "smallint":
 | 
| 
 | 
   107 				case "integer2":
 | 
| 
 | 
   108 					return OleDbType.SmallInt;
 | 
| 
 | 
   109 
 | 
| 
 | 
   110 				case "int":
 | 
| 
 | 
   111 				case "integer":
 | 
| 
 | 
   112 				case "long":
 | 
| 
 | 
   113 				case "integer4":
 | 
| 
 | 
   114 				case "counter":
 | 
| 
 | 
   115 				case "identity":
 | 
| 
 | 
   116 				case "autoincrement":
 | 
| 
 | 
   117 					return OleDbType.Integer;
 | 
| 
 | 
   118 
 | 
| 
 | 
   119 				case "single":
 | 
| 
 | 
   120 				case "real":
 | 
| 
 | 
   121 				case "float4":
 | 
| 
 | 
   122 				case "ieeesingle":
 | 
| 
 | 
   123 					return OleDbType.Single;
 | 
| 
 | 
   124 
 | 
| 
 | 
   125 
 | 
| 
 | 
   126 				case "double":
 | 
| 
 | 
   127 				case "number":
 | 
| 
 | 
   128 				case "double precision":
 | 
| 
 | 
   129 				case "float":
 | 
| 
 | 
   130 				case "float8":
 | 
| 
 | 
   131 				case "ieeedouble":
 | 
| 
 | 
   132 					return OleDbType.Double;
 | 
| 
 | 
   133 
 | 
| 
 | 
   134 				case "currency":
 | 
| 
 | 
   135 				case "money":
 | 
| 
 | 
   136 					return OleDbType.Currency;
 | 
| 
 | 
   137 
 | 
| 
 | 
   138 				case "dec":
 | 
| 
 | 
   139 				case "decimal":
 | 
| 
 | 
   140 				case "numeric":
 | 
| 
 | 
   141 					return OleDbType.Decimal;
 | 
| 
 | 
   142 
 | 
| 
 | 
   143 				case "bit":
 | 
| 
 | 
   144 				case "yesno":
 | 
| 
 | 
   145 				case "logical":
 | 
| 
 | 
   146 				case "logical1":
 | 
| 
 | 
   147 					return OleDbType.Boolean;
 | 
| 
 | 
   148 
 | 
| 
 | 
   149 				case "datetime":
 | 
| 
 | 
   150 				case "date":
 | 
| 
 | 
   151 				case "time":
 | 
| 
 | 
   152 					return OleDbType.Date;
 | 
| 
 | 
   153 
 | 
| 
 | 
   154 				case "alphanumeric":
 | 
| 
 | 
   155 				case "char":
 | 
| 
 | 
   156 				case "character":
 | 
| 
 | 
   157 				case "character varying":
 | 
| 
 | 
   158 				case "national char":
 | 
| 
 | 
   159 				case "national char varying":
 | 
| 
 | 
   160 				case "national character":
 | 
| 
 | 
   161 				case "national character varying":
 | 
| 
 | 
   162 				case "nchar":
 | 
| 
 | 
   163 				case "string":
 | 
| 
 | 
   164 				case "text":
 | 
| 
 | 
   165 				case "varchar":
 | 
| 
 | 
   166 					return OleDbType.VarWChar;
 | 
| 
 | 
   167 
 | 
| 
 | 
   168 				case "longchar":
 | 
| 
 | 
   169 				case "longtext":
 | 
| 
 | 
   170 				case "memo":
 | 
| 
 | 
   171 				case "note":
 | 
| 
 | 
   172 				case "ntext":
 | 
| 
 | 
   173 					return OleDbType.LongVarWChar;
 | 
| 
 | 
   174 
 | 
| 
 | 
   175 				case "binary":
 | 
| 
 | 
   176 				case "varbinary":
 | 
| 
 | 
   177 				case "binary varying":
 | 
| 
 | 
   178 				case "bit varying":
 | 
| 
 | 
   179 					return OleDbType.VarBinary;
 | 
| 
 | 
   180 
 | 
| 
 | 
   181 				case "longbinary":
 | 
| 
 | 
   182 				case "image":
 | 
| 
 | 
   183 				case "general":
 | 
| 
 | 
   184 				case "oleobject":
 | 
| 
 | 
   185 					return OleDbType.LongVarBinary;
 | 
| 
 | 
   186 
 | 
| 
 | 
   187 				case "guid":
 | 
| 
 | 
   188 				case "uniqueidentifier":
 | 
| 
 | 
   189 					return OleDbType.Guid;
 | 
| 
 | 
   190 
 | 
| 
 | 
   191 				default:
 | 
| 
 | 
   192 					// Each release of Jet brings many new aliases to existing types.
 | 
| 
 | 
   193 					// This list may be outdated, please send a report to us.
 | 
| 
 | 
   194 					//
 | 
| 
 | 
   195 					throw new NotSupportedException("Unknown DB type '" + jetType + "'");
 | 
| 
 | 
   196 			}
 | 
| 
 | 
   197 		}
 | 
| 
 | 
   198 
 | 
| 
 | 
   199 		public override void AttachParameter(IDbCommand command, IDbDataParameter parameter)
 | 
| 
 | 
   200 		{
 | 
| 
 | 
   201 			// Do some magic to workaround 'Data type mismatch in criteria expression' error
 | 
| 
 | 
   202 			// in JET for some european locales.
 | 
| 
 | 
   203 			//
 | 
| 
 | 
   204 			if (parameter.Value is DateTime)
 | 
| 
 | 
   205 			{
 | 
| 
 | 
   206 				// OleDbType.DBTimeStamp is locale aware, OleDbType.Date is locale neutral.
 | 
| 
 | 
   207 				//
 | 
| 
 | 
   208 				((OleDbParameter)parameter).OleDbType = OleDbType.Date;
 | 
| 
 | 
   209 			}
 | 
| 
 | 
   210 			else if (parameter.Value is decimal)
 | 
| 
 | 
   211 			{
 | 
| 
 | 
   212 				// OleDbType.Decimal is locale aware, OleDbType.Currency is locale neutral.
 | 
| 
 | 
   213 				//
 | 
| 
 | 
   214 				((OleDbParameter)parameter).OleDbType = OleDbType.Currency;
 | 
| 
 | 
   215 			}
 | 
| 
 | 
   216 
 | 
| 
 | 
   217 			base.AttachParameter(command, parameter);
 | 
| 
 | 
   218 		}
 | 
| 
 | 
   219 
 | 
| 
 | 
   220 		public new const string NameString = DataProvider.ProviderName.Access;
 | 
| 
 | 
   221 
 | 
| 
 | 
   222 		public override string Name
 | 
| 
 | 
   223 		{
 | 
| 
 | 
   224 			get { return NameString; }
 | 
| 
 | 
   225 		}
 | 
| 
 | 
   226 
 | 
| 
 | 
   227 		public override int MaxBatchSize
 | 
| 
 | 
   228 		{
 | 
| 
 | 
   229 			get { return 0; }
 | 
| 
 | 
   230 		}
 | 
| 
 | 
   231 
 | 
| 
 | 
   232 		public override ISqlProvider CreateSqlProvider()
 | 
| 
 | 
   233 		{
 | 
| 
 | 
   234 			return new AccessSqlProvider();
 | 
| 
 | 
   235 		}
 | 
| 
 | 
   236 
 | 
| 
 | 
   237 		public override object Convert(object value, ConvertType convertType)
 | 
| 
 | 
   238 		{
 | 
| 
 | 
   239 			switch (convertType)
 | 
| 
 | 
   240 			{
 | 
| 
 | 
   241 				case ConvertType.ExceptionToErrorNumber:
 | 
| 
 | 
   242 					if (value is OleDbException)
 | 
| 
 | 
   243 					{
 | 
| 
 | 
   244 						var ex = (OleDbException)value;
 | 
| 
 | 
   245 						if (ex.Errors.Count > 0)
 | 
| 
 | 
   246 							return ex.Errors[0].NativeError;
 | 
| 
 | 
   247 					}
 | 
| 
 | 
   248 
 | 
| 
 | 
   249 					break;
 | 
| 
 | 
   250 			}
 | 
| 
 | 
   251 
 | 
| 
 | 
   252 			return SqlProvider.Convert(value, convertType);
 | 
| 
 | 
   253 		}
 | 
| 
 | 
   254 
 | 
| 
 | 
   255 		#region DataReaderEx
 | 
| 
 | 
   256 
 | 
| 
 | 
   257 		public override IDataReader GetDataReader(MappingSchema schema, IDataReader dataReader)
 | 
| 
 | 
   258 		{
 | 
| 
 | 
   259 			return dataReader is OleDbDataReader?
 | 
| 
 | 
   260 				new DataReaderEx((OleDbDataReader)dataReader):
 | 
| 
 | 
   261 				base.GetDataReader(schema, dataReader);
 | 
| 
 | 
   262 		}
 | 
| 
 | 
   263 
 | 
| 
 | 
   264 		class DataReaderEx : DataReaderBase<OleDbDataReader>, IDataReader
 | 
| 
 | 
   265 		{
 | 
| 
 | 
   266 			public DataReaderEx(OleDbDataReader rd): base(rd)
 | 
| 
 | 
   267 			{
 | 
| 
 | 
   268 			}
 | 
| 
 | 
   269 
 | 
| 
 | 
   270 			public new object GetValue(int i)
 | 
| 
 | 
   271 			{
 | 
| 
 | 
   272 				var value = DataReader.GetValue(i);
 | 
| 
 | 
   273 
 | 
| 
 | 
   274 				if (value is DateTime)
 | 
| 
 | 
   275 				{
 | 
| 
 | 
   276 					var dt = (DateTime)value;
 | 
| 
 | 
   277 
 | 
| 
 | 
   278 					if (dt.Year == 1899 && dt.Month == 12 && dt.Day == 30)
 | 
| 
 | 
   279 						return new DateTime(1, 1, 1, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
 | 
| 
 | 
   280 				}
 | 
| 
 | 
   281 
 | 
| 
 | 
   282 				return value;
 | 
| 
 | 
   283 			}
 | 
| 
 | 
   284 
 | 
| 
 | 
   285 			public new DateTime GetDateTime(int i)
 | 
| 
 | 
   286 			{
 | 
| 
 | 
   287 				var dt = DataReader.GetDateTime(i);
 | 
| 
 | 
   288 
 | 
| 
 | 
   289 				if (dt.Year == 1899 && dt.Month == 12 && dt.Day == 30)
 | 
| 
 | 
   290 					return new DateTime(1, 1, 1, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
 | 
| 
 | 
   291 
 | 
| 
 | 
   292 				return dt;
 | 
| 
 | 
   293 			}
 | 
| 
 | 
   294 		}
 | 
| 
 | 
   295 
 | 
| 
 | 
   296 		#endregion
 | 
| 
 | 
   297 	}
 | 
| 
 | 
   298 }
 |