Mercurial > pub > bltoolkit
diff Source/Data/DataProvider/FdpDataProvider.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Data/DataProvider/FdpDataProvider.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,326 @@ +/*** + + * FdpDataProvider +needed FirebirdClient http://sourceforge.net/project/showfiles.php?group_id=9028&package_id=62107 +tested with FirebirdClient 2.1.0 Beta 3 + +Known troubles: +1) Some tests fails due to Fb SQL-syntax specific +2) ResultSet mapping doesn't work - not supported by client +3) UnitTests.CS.DataAccess.OutRefTest tests: Test2 && TestNullable2 doesnt work: + parameters directions should be provided correctly to functions run, that's why + output parameterd would be mapped to Entity e, so asserts should be same a in Test1. + +"Features" +1) Type conversation due to http://www.firebirdsql.org/manual/migration-mssql-data-types.html + BUT! for Binary types BLOB is used! not CHAR! +2) InOut parameters faking: InOut parameters are not suppotred by Fb, but they could be + emulated: each InOut parameter should be defined in RETURNS() section, and allso has a mirror + in parameter section with name [prefix][inOutParameterName], see OutRefTest SP. Faking settings: + FdpDataProvider.InOutInputParameterPrefix = "in_"; + FdpDataProvider.IsInOutParameterEmulation = true; +3) Returned values faking. Each parameter with "magic name" woul be treated as ReturnValue. + see Scalar_ReturnParameter SP. Faking settings: + FdpDataProvider.ReturnParameterName = "RETURN_VALUE"; + FdpDataProvider.IsReturnValueEmulation = true; + + */ + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Data.Common; +using System.Linq; + +using BLToolkit.Data.Sql.SqlProvider; +using BLToolkit.Mapping; +using BLToolkit.Reflection; + +using FirebirdSql.Data.FirebirdClient; + +namespace BLToolkit.Data.DataProvider +{ + public class FdpDataProvider : DataProviderBase + { + public FdpDataProvider() + { + MappingSchema = new FbMappingSchema(); + } + + #region InOut & ReturnValue emulation + public static string InOutInputParameterPrefix = "in_"; + public static string ReturnParameterName = "RETURN_VALUE"; + + public static bool IsReturnValueEmulation = true; + public static bool IsInOutParameterEmulation = true; + + public static bool QuoteIdentifiers + { + get { return FirebirdSqlProvider.QuoteIdentifiers; } + set { FirebirdSqlProvider.QuoteIdentifiers = value; } + } + #endregion + + #region Overloads + public override Type ConnectionType + { + get { return typeof (FbConnection); } + } + + public override string Name + { + get { return DataProvider.ProviderName.Firebird; } + } + + public override int MaxBatchSize + { + get { return 0; } + } + + public override IDbConnection CreateConnectionObject() + { + return new FbConnection(); + } + + public override DbDataAdapter CreateDataAdapterObject() + { + return new FbDataAdapter(); + } + + public override bool DeriveParameters(IDbCommand command) + { + if (command is FbCommand) + { + FbCommandBuilder.DeriveParameters((FbCommand) command); + + if (IsReturnValueEmulation) + foreach (IDbDataParameter par in command.Parameters) + if (IsReturnValue(par)) + par.Direction = ParameterDirection.ReturnValue; + + return true; + } + + return false; + } + + public override object Convert(object value, ConvertType convertType) + { + switch (convertType) + { + case ConvertType.ExceptionToErrorNumber: + if (value is FbException) + { + var ex = (FbException) value; + if (ex.Errors.Count > 0) + foreach (FbError error in ex.Errors) + return error.Number; + } + + break; + } + + return SqlProvider.Convert(value, convertType); + } + + public override ISqlProvider CreateSqlProvider() + { + return new FirebirdSqlProvider(); + } + + public override bool IsValueParameter(IDbDataParameter parameter) + { + return parameter.Direction != ParameterDirection.ReturnValue + && parameter.Direction != ParameterDirection.Output; + } + + private string GetInputParameterName(string ioParameterName) + { + return (string) Convert( + InOutInputParameterPrefix + (string) Convert(ioParameterName, ConvertType.SprocParameterToName), + ConvertType.NameToSprocParameter); + } + + private static IDbDataParameter GetParameter(string parameterName, IEnumerable<IDbDataParameter> commandParameters) + { + return commandParameters.FirstOrDefault(par => string.Compare(parameterName, par.ParameterName, true) == 0); + } + + private bool IsReturnValue(IDbDataParameter parameter) + { + if (string.Compare(parameter.ParameterName, + (string) Convert(ReturnParameterName, ConvertType.NameToSprocParameter), true) == 0 + ) + return true; + + return false; + } + + public override void PrepareCommand(ref CommandType commandType, ref string commandText, + ref IDbDataParameter[] commandParameters) + { + if (commandParameters != null) + foreach (var par in commandParameters) + { + if (par.Value is bool) + { + var value = (bool) par.Value ? "1" : "0"; + + par.DbType = DbType.AnsiString; + par.Value = value; + par.Size = value.Length; + } + else if (par.Value is Guid) + { + var value = par.Value.ToString(); + + par.DbType = DbType.AnsiStringFixedLength; + par.Value = value; + par.Size = value.Length; + } + + #region "smart" input-output parameter detection + if (commandType == CommandType.StoredProcedure && IsInOutParameterEmulation) + { + var iParameterName = GetInputParameterName(par.ParameterName); + var fakeIOParameter = GetParameter(iParameterName, commandParameters); + + if (fakeIOParameter != null) + { + fakeIOParameter.Value = par.Value; + + // direction should be output, or parameter mistmath for procedure exception + // would be thrown + par.Direction = ParameterDirection.Output; + + // direction should be Input + fakeIOParameter.Direction = ParameterDirection.Input; + } + } + #endregion + } + + base.PrepareCommand(ref commandType, ref commandText, ref commandParameters); + } + + public override bool InitParameter(IDbDataParameter parameter) + { + if (parameter.Value is bool) + { + var value = (bool) parameter.Value ? "1" : "0"; + + parameter.DbType = DbType.AnsiString; + parameter.Value = value; + parameter.Size = value.Length; + } + else if (parameter.Value is Guid) + { + var value = parameter.Value.ToString(); + + parameter.DbType = DbType.AnsiStringFixedLength; + parameter.Value = value; + parameter.Size = value.Length; + } + + return base.InitParameter(parameter); + } + + public override void Configure(NameValueCollection attributes) + { + var inOutInputParameterPrefix = attributes["InOutInputParameterPrefix"]; + if (inOutInputParameterPrefix != null) + InOutInputParameterPrefix = inOutInputParameterPrefix; + + var returnParameterName = attributes["ReturnParameterName"]; + if (returnParameterName != null) + ReturnParameterName = returnParameterName; + + var isReturnValueEmulation = attributes["IsReturnValueEmulation"]; + if (isReturnValueEmulation != null) + IsReturnValueEmulation = Common.Convert.ToBoolean(isReturnValueEmulation); + + var isInOutParameterEmulation = attributes["IsInOutParameterEmulation"]; + if (isInOutParameterEmulation != null) + IsInOutParameterEmulation = Common.Convert.ToBoolean(isInOutParameterEmulation); + + var quoteIdentifiers = attributes["QuoteIdentifiers"]; + if (quoteIdentifiers != null) + QuoteIdentifiers = Common.Convert.ToBoolean(quoteIdentifiers); + + base.Configure(attributes); + } + #endregion + + #region FbDataReaderEx + public override IDataReader GetDataReader(MappingSchema schema, IDataReader dataReader) + { + return + dataReader is FbDataReader + ? new FbDataReaderEx((FbDataReader) dataReader) + : base.GetDataReader(schema, dataReader); + } + + private class FbDataReaderEx : DataReaderBase<FbDataReader>, IDataReader + { + public FbDataReaderEx(FbDataReader rd) : base(rd) + { + } + + #region IDataReader Members + public new object GetValue(int i) + { + var value = DataReader.GetValue(i); + + if (value is DateTime) + { + var dt = (DateTime) value; + + if (dt.Year == 1970 && dt.Month == 1 && dt.Day == 1) + return new DateTime(1, 1, 1, dt.Hour, dt.Minute, dt.Second, dt.Millisecond); + } + + return value; + } + + public new DateTime GetDateTime(int i) + { + var dt = DataReader.GetDateTime(i); + + if (dt.Year == 1970 && dt.Month == 1 && dt.Day == 1) + return new DateTime(1, 1, 1, dt.Hour, dt.Minute, dt.Second, dt.Millisecond); + + return dt; + } + #endregion + } + #endregion + + #region FbMappingSchema + public class FbMappingSchema : FirebirdMappingSchema + { + protected override object MapInternal(InitContext initContext) + { + var dr = initContext.SourceObject as FbDataReader; + + // Fb's SP returns single row with nulls if selected object doesn't exists + // so for all DBNull's (null) should be returned, instead of object instance + // + if (dr != null) + { + var i = dr.FieldCount; + while (--i >= 0) + if (!dr.IsDBNull(i)) + break; + + // All field are DBNull. + // + if (i < 0) + return null; + } + return base.MapInternal(initContext); + } + } + #endregion + } +} \ No newline at end of file