public object GetRawValueSequential(IDataReader dataReader, Type[] forTypes) { var fromType = dataReader.GetFieldType(ColumnIndex); if (!_slowRawReaders.TryGetValue(fromType, out var func)) { var dataReaderParameter = Parameter(typeof(IDataReader)); var dataReaderExpr = Convert(dataReaderParameter, dataReader.GetType()); MethodCallExpression rawExpr = null !; foreach (var type in forTypes) { var expr = GetColumnReader(_dataContext, _mappingSchema, dataReader, type, _converter, ColumnIndex, dataReaderExpr, _slowMode); var currentRawExpr = SequentialAccessHelper.ExtractRawValueReader(expr, ColumnIndex); if (rawExpr == null) { rawExpr = currentRawExpr; } else if (rawExpr.Method != currentRawExpr.Method) { throw new LinqToDBConvertException( $"Different data reader methods used for same column: '{rawExpr.Method.DeclaringType?.Name}.{rawExpr.Method.Name}' vs '{currentRawExpr.Method.DeclaringType?.Name}.{currentRawExpr.Method.Name}'"); } } var lex = Lambda <Func <IDataReader, object> >( rawExpr.Type == typeof(object) ? rawExpr : Convert(rawExpr, typeof(object)), dataReaderParameter); _slowRawReaders[fromType] = func = lex.CompileExpression(); } return(func(dataReader)); }
public Func <IDataReader, T> BuildReaderFunction <T>() { var expr = BuildReaderExpression(); var lambda = Expression.Lambda <Func <IDataReader, T> >(BuildBlock(expr), DataReaderParam); if (Common.Configuration.OptimizeForSequentialAccess) { lambda = (Expression <Func <IDataReader, T> >)SequentialAccessHelper.OptimizeMappingExpressionForSequentialAccess(lambda, Reader.FieldCount, reduce: true); } return(lambda.Compile()); }
/* * We could have column readers for same column with different ColumnType types which results in different * reader expressions. * To make it work with sequential mode we should perform actual column value read from reader only * once and then use it in reader expressions for all types. * For that we add additional method to read raw value and then pass it to GetValueSequential. * We need extra method as we cannot store raw value in field: ColumnReader instance could be used * from multiple threads, so it cannot have state. For same reason it doesn't make much sense to reduce number * of ColumnReader instances in mapper expression to one for single column. It could be done later if we will * see benefits of it, but frankly speaking it doesn't make sense to optimize slow-mode reader. * * Limitation is the same as for non-slow mapper: * column mapping expressions should use same reader method to get column value. This limitation enforced * in GetRawValueSequential method. */ public object?GetValueSequential(IDataReader dataReader, bool isNull, object?rawValue) { var fromType = dataReader.GetFieldType(ColumnIndex); if (!_slowColumnConverters.TryGetValue(fromType, out var func)) { var dataReaderParameter = Parameter(typeof(IDataReader)); var isNullParameter = Parameter(typeof(bool)); var rawValueParameter = Parameter(typeof(object)); var dataReaderExpr = Convert(dataReaderParameter, dataReader.GetType()); var expr = GetColumnReader(_dataContext, _mappingSchema, dataReader, ColumnType, _converter, ColumnIndex, dataReaderExpr, _slowMode); expr = SequentialAccessHelper.OptimizeColumnReaderForSequentialAccess(expr, isNullParameter, rawValueParameter, ColumnIndex); var lex = Lambda <Func <bool, object?, object?> >( expr.Type == typeof(object) ? expr : Convert(expr, typeof(object)), isNullParameter, rawValueParameter); _slowColumnConverters[fromType] = func = lex.CompileExpression(); } try { return(func(isNull, rawValue)); } catch (LinqToDBConvertException ex) { ex.ColumnName = dataReader.GetName(ColumnIndex); throw; } catch (Exception ex) { var name = dataReader.GetName(ColumnIndex); throw new LinqToDBConvertException( $"Mapping of column '{name}' value failed, see inner exception for details", ex) { ColumnName = name }; } }