private static OracleProviderAdapter CreateAdapter(string assemblyName, string clientNamespace, string typesNamespace, string?factoryName) { var isNative = false; #if NETFRAMEWORK isNative = assemblyName == NativeAssemblyName; #endif var assembly = Common.Tools.TryLoadAssembly(assemblyName, factoryName); if (assembly == null) { throw new InvalidOperationException($"Cannot load assembly {assemblyName}"); } var connectionType = assembly.GetType($"{clientNamespace}.OracleConnection", true) !; var parameterType = assembly.GetType($"{clientNamespace}.OracleParameter", true) !; var dataReaderType = assembly.GetType($"{clientNamespace}.OracleDataReader", true) !; var transactionType = assembly.GetType($"{clientNamespace}.OracleTransaction", true) !; var dbType = assembly.GetType($"{clientNamespace}.OracleDbType", true) !; var commandType = assembly.GetType($"{clientNamespace}.OracleCommand", true) !; var mappingSchema = new MappingSchema(); // do not set default conversion for BFile as it could be converted to file name, byte[], Stream and we don't know what user needs var oracleBFileType = loadType("OracleBFile", DataType.BFile, skipConvertExpression: true) !; var oracleBinaryType = loadType("OracleBinary", DataType.VarBinary) !; var oracleBlobType = loadType("OracleBlob", DataType.Blob) !; var oracleClobType = loadType("OracleClob", DataType.NText) !; var oracleDateType = loadType("OracleDate", DataType.DateTime) !; var oracleDecimalType = loadType("OracleDecimal", DataType.Decimal) !; var oracleIntervalDSType = loadType("OracleIntervalDS", DataType.Time) !; var oracleIntervalYMType = loadType("OracleIntervalYM", DataType.Date) !; var oracleStringType = loadType("OracleString", DataType.NVarChar) !; var oracleTimeStampType = loadType("OracleTimeStamp", DataType.DateTime2) !; var oracleTimeStampLTZType = loadType("OracleTimeStampLTZ", DataType.DateTimeOffset) !; var oracleTimeStampTZType = loadType("OracleTimeStampTZ", DataType.DateTimeOffset) !; var oracleXmlTypeType = loadType("OracleXmlType", DataType.Xml) !; var oracleXmlStreamType = loadType("OracleXmlStream", DataType.Xml, true, false) !; var oracleRefCursorType = loadType("OracleRefCursor", DataType.Binary, hasValue: false) !; var oracleRefType = loadType("OracleRef", DataType.Binary, true); BulkCopyAdapter?bulkCopy = null; var typeMapper = new TypeMapper(); typeMapper.RegisterTypeWrapper <OracleConnection>(connectionType); typeMapper.RegisterTypeWrapper <OracleParameter>(parameterType); typeMapper.RegisterTypeWrapper <OracleDbType>(dbType); typeMapper.RegisterTypeWrapper <OracleCommand>(commandType); typeMapper.RegisterTypeWrapper <OracleDataReader>(dataReaderType); typeMapper.RegisterTypeWrapper <OracleTimeStampTZ>(oracleTimeStampTZType); typeMapper.RegisterTypeWrapper <OracleTimeStampLTZ>(oracleTimeStampLTZType); typeMapper.RegisterTypeWrapper <OracleDecimal>(oracleDecimalType); if (isNative) { var bulkCopyType = assembly.GetType($"{clientNamespace}.OracleBulkCopy", true) !; var bulkCopyOptionsType = assembly.GetType($"{clientNamespace}.OracleBulkCopyOptions", true) !; var bulkRowsCopiedEventHandlerType = assembly.GetType($"{clientNamespace}.OracleRowsCopiedEventHandler", true) !; var bulkCopyColumnMappingType = assembly.GetType($"{clientNamespace}.OracleBulkCopyColumnMapping", true) !; var bulkCopyColumnMappingCollectionType = assembly.GetType($"{clientNamespace}.OracleBulkCopyColumnMappingCollection", true) !; var rowsCopiedEventArgsType = assembly.GetType($"{clientNamespace}.OracleRowsCopiedEventArgs", true) !; // bulk copy types typeMapper.RegisterTypeWrapper <OracleBulkCopy>(bulkCopyType); typeMapper.RegisterTypeWrapper <OracleBulkCopyOptions>(bulkCopyOptionsType); typeMapper.RegisterTypeWrapper <OracleRowsCopiedEventHandler>(bulkRowsCopiedEventHandlerType); typeMapper.RegisterTypeWrapper <OracleBulkCopyColumnMapping>(bulkCopyColumnMappingType); typeMapper.RegisterTypeWrapper <OracleBulkCopyColumnMappingCollection>(bulkCopyColumnMappingCollectionType); typeMapper.RegisterTypeWrapper <OracleRowsCopiedEventArgs>(rowsCopiedEventArgsType); typeMapper.FinalizeMappings(); bulkCopy = new BulkCopyAdapter( typeMapper.BuildWrappedFactory((IDbConnection connection, OracleBulkCopyOptions options) => new OracleBulkCopy((OracleConnection)connection, options)), typeMapper.BuildWrappedFactory((int source, string destination) => new OracleBulkCopyColumnMapping(source, destination))); } else { typeMapper.FinalizeMappings(); } var paramMapper = typeMapper.Type <OracleParameter>(); var dbTypeBuilder = paramMapper.Member(p => p.OracleDbType); var connectionMapper = typeMapper.Type <OracleConnection>(); var commandMapper = typeMapper.Type <OracleCommand>(); // data reader expressions // rd.GetOracleTimeStampTZ(i) => DateTimeOffset var generator = new ExpressionGenerator(typeMapper); var rdParam = Expression.Parameter(typeof(IDataReader), "rd"); var indexParam = Expression.Parameter(typeof(int), "i"); var tstzExpr = generator.MapExpression((IDataReader rd, int i) => ((OracleDataReader)rd).GetOracleTimeStampTZ(i), rdParam, indexParam); var tstzVariable = generator.AssignToVariable(tstzExpr, "tstz"); var expr = generator.MapExpression((OracleTimeStampTZ tstz) => new DateTimeOffset( tstz.Year, tstz.Month, tstz.Day, tstz.Hour, tstz.Minute, tstz.Second, tstz.GetTimeZoneOffset()).AddTicks(tstz.Nanosecond / NanosecondsPerTick), tstzVariable); generator.AddExpression(expr); var body = generator.Build(); var readDateTimeOffsetFromOracleTimeStampTZ = (Expression <Func <IDataReader, int, DateTimeOffset> >)Expression.Lambda(body, rdParam, indexParam); // rd.GetOracleTimeStampLTZ(i) => DateTimeOffset generator = new ExpressionGenerator(typeMapper); tstzExpr = generator.MapExpression((IDataReader rd, int i) => ((OracleDataReader)rd).GetOracleTimeStampLTZ(i).ToOracleTimeStampTZ(), rdParam, indexParam); tstzVariable = generator.AssignToVariable(tstzExpr, "tstz"); expr = generator.MapExpression((OracleTimeStampTZ tstz) => new DateTimeOffset( tstz.Year, tstz.Month, tstz.Day, tstz.Hour, tstz.Minute, tstz.Second, tstz.GetTimeZoneOffset()).AddTicks(tstz.Nanosecond / NanosecondsPerTick), tstzVariable); generator.AddExpression(expr); body = generator.Build(); var readDateTimeOffsetFromOracleTimeStampLTZ = (Expression <Func <IDataReader, int, DateTimeOffset> >)Expression.Lambda(body, rdParam, indexParam); // rd.GetOracleDecimal(i) => decimal generator = new ExpressionGenerator(typeMapper); var decExpr = generator.MapExpression((IDataReader rd, int i) => ((OracleDataReader)rd).GetOracleDecimal(i), rdParam, indexParam); var oracleDecimalVar = generator.AssignToVariable(decExpr, "dec"); var precision = generator.AssignToVariable(Expression.Constant(29), "precision"); var decimalVar = generator.AddVariable(Expression.Parameter(typeof(decimal), "dec")); var label = Expression.Label(typeof(decimal)); generator.AddExpression( Expression.Loop( Expression.TryCatch( Expression.Block( Expression.Assign(oracleDecimalVar, generator.MapExpression((OracleDecimal d, int p) => OracleDecimal.SetPrecision(d, p), oracleDecimalVar, precision)), Expression.Assign(decimalVar, Expression.Convert(oracleDecimalVar, typeof(decimal))), Expression.Break(label, decimalVar)), Expression.Catch( typeof(OverflowException), Expression.Block( Expression.IfThen( Expression.LessThanOrEqual(Expression.SubtractAssign(precision, Expression.Constant(1)), Expression.Constant(26)), Expression.Rethrow())))), label)); body = generator.Build(); var readOracleDecimalToDecimalAdv = (Expression <Func <IDataReader, int, decimal> >)Expression.Lambda(body, rdParam, indexParam); // workaround for mapper issue with complex reader expressions handling // https://github.com/linq2db/linq2db/issues/2032 var compiledReader = readOracleDecimalToDecimalAdv.Compile(); readOracleDecimalToDecimalAdv = (Expression <Func <IDataReader, int, decimal> >)Expression.Lambda( Expression.Invoke(Expression.Constant(compiledReader), rdParam, indexParam), rdParam, indexParam); var readOracleDecimalToInt = (Expression <Func <IDataReader, int, int> >)typeMapper.MapLambda <IDataReader, int, int>((rd, i) => (int)(decimal)OracleDecimal.SetPrecision(((OracleDataReader)rd).GetOracleDecimal(i), 27)); var readOracleDecimalToLong = (Expression <Func <IDataReader, int, long> >)typeMapper.MapLambda <IDataReader, int, long>((rd, i) => (long)(decimal)OracleDecimal.SetPrecision(((OracleDataReader)rd).GetOracleDecimal(i), 27)); var readOracleDecimalToDecimal = (Expression <Func <IDataReader, int, decimal> >)typeMapper.MapLambda <IDataReader, int, decimal>((rd, i) => (decimal)OracleDecimal.SetPrecision(((OracleDataReader)rd).GetOracleDecimal(i), 27)); return(new OracleProviderAdapter( connectionType, dataReaderType, parameterType, commandType, transactionType, mappingSchema, oracleBFileType, oracleBinaryType, oracleBlobType, oracleClobType, oracleDateType, oracleDecimalType, oracleIntervalDSType, oracleIntervalYMType, oracleStringType, oracleTimeStampType, oracleTimeStampLTZType, oracleTimeStampTZType, oracleXmlTypeType, oracleXmlStreamType, oracleRefCursorType, oracleRefType, typeMapper.BuildWrappedFactory((string connectionString) => new OracleConnection(connectionString)), typesNamespace, dbTypeBuilder.BuildSetter <IDbDataParameter>(), dbTypeBuilder.BuildGetter <IDbDataParameter>(), connectionMapper.Member(c => c.HostName).BuildGetter <IDbConnection>(), connectionMapper.Member(c => c.DatabaseName).BuildGetter <IDbConnection>(), commandMapper.Member(p => p.BindByName).BuildSetter <IDbCommand>(), commandMapper.Member(p => p.ArrayBindCount).BuildSetter <IDbCommand>(), commandMapper.Member(p => p.InitialLONGFetchSize).BuildSetter <IDbCommand>(), typeMapper.BuildFactory((DateTimeOffset dto, string offset) => new OracleTimeStampTZ(dto.Year, dto.Month, dto.Day, dto.Hour, dto.Minute, dto.Second, GetDateTimeOffsetNanoseconds(dto), offset)), readDateTimeOffsetFromOracleTimeStampTZ, readDateTimeOffsetFromOracleTimeStampLTZ, readOracleDecimalToDecimalAdv, readOracleDecimalToInt, readOracleDecimalToLong, readOracleDecimalToDecimal, bulkCopy)); Type?loadType(string typeName, DataType dataType, bool optional = false, bool hasNull = true, bool hasValue = true, bool skipConvertExpression = false) { var type = assembly !.GetType($"{typesNamespace}.{typeName}", !optional); if (type == null) { return(null); } if (hasNull) { // if native provider fails here, check that you have ODAC installed properly var getNullValue = Expression.Lambda <Func <object> >(Expression.Convert(ExpressionHelper.Field(type, "Null"), typeof(object))).Compile(); mappingSchema.AddScalarType(type, getNullValue(), true, dataType); } else { mappingSchema.AddScalarType(type, null, true, dataType); } if (skipConvertExpression) { return(type); } // conversion from provider-specific type var valueParam = Expression.Parameter(type); Expression memberExpression; if (!hasValue) { memberExpression = valueParam; } else { memberExpression = ExpressionHelper.Property(valueParam, "Value"); } var condition = Expression.Condition( Expression.Equal(valueParam, ExpressionHelper.Field(type, "Null")), Expression.Constant(null, typeof(object)), Expression.Convert(memberExpression, typeof(object))); var convertExpression = Expression.Lambda(condition, valueParam); mappingSchema.SetConvertExpression(type, typeof(object), convertExpression); return(type); } }
private static InformixProviderAdapter CreateIfxAdapter() { var assembly = Common.Tools.TryLoadAssembly(IfxAssemblyName, IfxProviderFactoryName); if (assembly == null) { throw new InvalidOperationException($"Cannot load assembly {IfxAssemblyName}"); } var connectionType = assembly.GetType($"{IfxClientNamespace}.IfxConnection", true); var parameterType = assembly.GetType($"{IfxClientNamespace}.IfxParameter", true); var dataReaderType = assembly.GetType($"{IfxClientNamespace}.IfxDataReader", true); var commandType = assembly.GetType($"{IfxClientNamespace}.IfxCommand", true); var transactionType = assembly.GetType($"{IfxClientNamespace}.IfxTransaction", true); var dbType = assembly.GetType($"{IfxClientNamespace}.IfxType", true); var mappingSchema = new MappingSchema(); var blobType = loadType("IfxBlob", DataType.VarBinary) !; var clobType = loadType("IfxClob", DataType.Text) !; var dateTimeType = loadType("IfxDateTime", DataType.DateTime2) !; // those two types obsoleted in recent providers var decimalType = loadType("IfxDecimal", DataType.Decimal) !; var timeSpanType = loadType("IfxTimeSpan", DataType.Time, true, true); var typeMapper = new TypeMapper(); typeMapper.RegisterTypeWrapper <IfxConnection>(connectionType); typeMapper.RegisterTypeWrapper <IfxParameter>(parameterType); typeMapper.RegisterTypeWrapper <IfxType>(dbType); if (timeSpanType != null) { typeMapper.RegisterTypeWrapper <IfxTimeSpan>(timeSpanType); } // bulk copy exists only for IDS provider version BulkCopyAdapter?bulkCopy = null; var bulkCopyType = assembly.GetType($"{IfxClientNamespace}.IfxBulkCopy", false); if (bulkCopyType != null) { var bulkCopyOptionsType = assembly.GetType($"{IfxClientNamespace}.IfxBulkCopyOptions", true); var bulkRowsCopiedEventHandlerType = assembly.GetType($"{IfxClientNamespace}.IfxRowsCopiedEventHandler", true); var bulkCopyColumnMappingType = assembly.GetType($"{IfxClientNamespace}.IfxBulkCopyColumnMapping", true); var bulkCopyColumnMappingCollectionType = assembly.GetType($"{IfxClientNamespace}.IfxBulkCopyColumnMappingCollection", true); var rowsCopiedEventArgsType = assembly.GetType($"{IfxClientNamespace}.IfxRowsCopiedEventArgs", true); typeMapper.RegisterTypeWrapper <IfxBulkCopy>(bulkCopyType); typeMapper.RegisterTypeWrapper <IfxBulkCopyOptions>(bulkCopyOptionsType); typeMapper.RegisterTypeWrapper <IfxRowsCopiedEventHandler>(bulkRowsCopiedEventHandlerType); typeMapper.RegisterTypeWrapper <IfxBulkCopyColumnMapping>(bulkCopyColumnMappingType); typeMapper.RegisterTypeWrapper <IfxBulkCopyColumnMappingCollection>(bulkCopyColumnMappingCollectionType); typeMapper.RegisterTypeWrapper <IfxRowsCopiedEventArgs>(rowsCopiedEventArgsType); typeMapper.FinalizeMappings(); bulkCopy = new BulkCopyAdapter( typeMapper.BuildWrappedFactory((IDbConnection connection, IfxBulkCopyOptions options) => new IfxBulkCopy((IfxConnection)connection, options)), typeMapper.BuildWrappedFactory((int source, string destination) => new IfxBulkCopyColumnMapping(source, destination))); } else { typeMapper.FinalizeMappings(); } var paramMapper = typeMapper.Type <IfxParameter>(); var dbTypeBuilder = paramMapper.Member(p => p.IfxType); Func <TimeSpan, object>?timespanFactory = null; if (timeSpanType != null) { timespanFactory = typeMapper.BuildFactory((TimeSpan ts) => new IfxTimeSpan(ts)); } return(new InformixProviderAdapter( connectionType, dataReaderType, parameterType, commandType, transactionType, mappingSchema, blobType, clobType, decimalType, dateTimeType, timeSpanType, dbTypeBuilder.BuildSetter <IDbDataParameter>(), dbTypeBuilder.BuildGetter <IDbDataParameter>(), timespanFactory, bulkCopy)); Type?loadType(string typeName, DataType dataType, bool optional = false, bool obsolete = false, bool register = true) { var type = assembly !.GetType($"{IfxTypesNamespace}.{typeName}", !optional); if (type == null) { return(null); } if (obsolete && type.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length > 0) { return(null); } if (register) { var getNullValue = Expression.Lambda <Func <object> >(Expression.Convert(ExpressionHelper.Field(type, "Null"), typeof(object))).Compile(); mappingSchema.AddScalarType(type, getNullValue(), true, dataType); } return(type); } }