public Expression CreateExpression(Expression source, Expression reader, int index) { var jsonTextReaderVariable = Expression.Variable(typeof(JsonTextReader), "jsonTextReader"); var resultVariable = Expression.Variable(source.Type, "result"); var materializer = new ComplexTypeMaterializerBuildingExpressionVisitor(jsonTextReaderVariable, typeMappingProvider) .Visit(source); return(Expression.Condition( Expression.Call(reader, dbDataReaderIsDBNullMethodInfo, Expression.Constant(index)), Expression.Convert(CreateDefaultValueExpression(source.Type), source.Type), Expression.Call( GetType() .GetMethod(nameof(Materialize), BindingFlags.Static | BindingFlags.NonPublic) .MakeGenericMethod(source.Type), Expression.Call( reader, dbDataReaderGetTextReaderMethodInfo, Expression.Constant(index)), Expression.Lambda( materializer, "JsonMaterializer", new[] { jsonTextReaderVariable })))); }
protected override Expression VisitLeaf(Expression node) { if (node.Type.IsScalarType()) { var temporaryVariableExpression = Expression.Variable(node.Type); return(Expression.Block( variables: new[] { temporaryVariableExpression }, expressions: new[] { readExpression, // Value Expression.Assign( temporaryVariableExpression, CreateScalarTypeReadExpression(node.Type, jsonTextReader)), readExpression, // PropertyName | EndObject temporaryVariableExpression, })); } else if (node.Type.IsSequenceType()) { var sequenceType = node.Type.GetSequenceType(); Expression materializerExpression; if (sequenceType.IsScalarType()) { materializerExpression = CreateScalarTypeReadExpression(sequenceType, jsonTextReader); } else { materializerExpression = new ComplexTypeMaterializerBuildingExpressionVisitor(jsonTextReader) .Visit(ExtractProjectionExpression(node)); } if (sequenceType.IsScalarType() || sequenceType.IsSequenceType()) { var temporaryVariableExpression = Expression.Variable(sequenceType); materializerExpression = Expression.Block( variables: new[] { temporaryVariableExpression }, expressions: new[] { readExpression, // PropertyName readExpression, // Value Expression.Assign(temporaryVariableExpression, materializerExpression), readExpression, // EndObject temporaryVariableExpression, }); } var listVariable = Expression.Variable(typeof(List <>).MakeGenericType(sequenceType)); var listAddMethod = listVariable.Type.GetRuntimeMethod(nameof(List <object> .Add), new[] { sequenceType }); var breakLabelTarget = Expression.Label(); var loopBody = Expression.IfThenElse( test: Expression.Equal( Expression.Property(jsonTextReader, jsonTextReaderTokenTypePropertyInfo), Expression.Constant(JsonToken.EndArray)), ifTrue: Expression.Break(breakLabelTarget), ifFalse: Expression.Block( expressions: new[] { Expression.Call(listVariable, listAddMethod, materializerExpression), readExpression, // StartObject | EndArray })); return(Expression.Block( variables: new[] { listVariable }, expressions: new[] { Expression.Assign(listVariable, Expression.New(listVariable.Type)), readExpression, // StartArray readExpression, // StartObject Expression.Loop(loopBody, breakLabelTarget), readExpression, // EndObject | PropertyName node.Type.IsArray ? Expression.Call( enumerableToArrayMethodInfo.MakeGenericMethod(sequenceType), listVariable) : node.Type.IsGenericType(typeof(IQueryable <>)) ? Expression.Call( queryableAsQueryableMethodInfo.MakeGenericMethod(sequenceType), listVariable) : listVariable as Expression, })); } else { return(new ComplexTypeMaterializerBuildingExpressionVisitor(jsonTextReader) .Visit(ExtractProjectionExpression(node))); } }
protected override Expression VisitLeaf(Expression node) { if (node.Type.IsScalarType()) { var sqlColumnExpression = node as SqlColumnExpression; var typeMapping = sqlColumnExpression?.TypeMapping ?? typeMappingProvider.FindMapping(node.Type); if (typeMapping?.TargetConversion is null) { return(SqlServerJsonValueReader.CreateReadScalarExpression( node.Type, jsonTextReader, GetNameParts().Last())); } var result = SqlServerJsonValueReader.CreateReadScalarExpression( typeMapping.SourceType, jsonTextReader, GetNameParts().Last()); var mappingParameter = typeMapping.TargetConversion.Parameters.Single(); result = Expression.Block( variables: new[] { mappingParameter }, expressions: new Expression[] { Expression.Assign(mappingParameter, result), Expression.Convert(typeMapping.TargetConversion.Body, node.Type), }); return(result); } else if (node.Type.IsSequenceType()) { var sequenceType = node.Type.GetSequenceType(); var extracted = ExtractProjectionExpression(node); Expression materializer; if (sequenceType.IsScalarType()) { // TODO: Get rid of the need to put this constant multiple places. var name = "$c"; switch (extracted) { case SqlColumnExpression sqlColumnExpression: { name = sqlColumnExpression.ColumnName; break; } case SqlAliasExpression sqlAliasExpression: { name = sqlAliasExpression.Alias; break; } } materializer = SqlServerJsonValueReader.CreateReadScalarExpression(sequenceType, jsonTextReader, name); } else { if (extracted == node) { return(SqlServerJsonValueReader.CreateReadOpaqueObjectExpression(node.Type, jsonTextReader)); } var visitor = new ComplexTypeMaterializerBuildingExpressionVisitor(jsonTextReader, typeMappingProvider); materializer = visitor.Visit(extracted); } return(CreateSequenceExpression( SqlServerJsonValueReader.CreateReadArrayExpression( sequenceType, jsonTextReader, GetNameParts().LastOrDefault(), Expression.Lambda( materializer, GetMaterializerName(), Array.Empty <ParameterExpression>())), node.Type)); } else { var extracted = ExtractProjectionExpression(node); if (extracted == node) { return(SqlServerJsonValueReader.CreateReadOpaqueObjectExpression(node.Type, jsonTextReader)); } var visitor = new ComplexTypeMaterializerBuildingExpressionVisitor(jsonTextReader, typeMappingProvider); var result = visitor.Visit(extracted); if (result is MethodCallExpression call && call.Method.DeclaringType == typeof(SqlServerJsonValueReader)) { return(result); } return(SqlServerJsonValueReader.CreateReadComplexObjectExpression( node.Type, jsonTextReader, Expression.Lambda( result, GetMaterializerName(), Array.Empty <ParameterExpression>()))); } }