public override Expression?Visit(Expression?expression) { if (this.candidates.Contains(expression !)) { if (expression is ColumnExpression column) { if (!projectTrivialColumns) { return(expression); } if (this.map.TryGetValue(column, out var mapped)) { return(mapped); } mapped = generator.MapColumn(column).GetReference(newAlias); this.map[column] = mapped; return(mapped); } else { if (expression !.Type.UnNullify().IsEnum) { var convert = expression.TryConvert(expression.Type.IsNullable() ? typeof(int?) : typeof(int)); return(generator.NewColumn(convert).GetReference(newAlias).TryConvert(expression.Type)); }
protected internal override Expression VisitProjection(ProjectionExpression proj) { if (currentSource == null) { currentSource = WithoutOrder(proj.Select); Expression projector = this.Visit(proj.Projector); if (projector != proj.Projector) { proj = new ProjectionExpression(proj.Select, projector, proj.UniqueFunction, proj.Type); } currentSource = null; return(proj); } else { HashSet <ColumnExpression> columns = ExternalColumnGatherer.Gatherer(proj, currentSource.Alias); if (columns.Count == 0) { Expression projector = Visit(proj.Projector); ConstantExpression key = Expression.Constant(0); Type kvpType = typeof(KeyValuePair <,>).MakeGenericType(key.Type, projector.Type); ConstructorInfo ciKVP = kvpType.GetConstructor(new[] { key.Type, projector.Type }) !; Type projType = proj.UniqueFunction == null ? typeof(IEnumerable <>).MakeGenericType(kvpType) : kvpType; var childProj = new ProjectionExpression(proj.Select, Expression.New(ciKVP, key, projector), proj.UniqueFunction, projType); return(new ChildProjectionExpression(childProj, Expression.Constant(0), inMList != null, inMList ?? proj.Type, new LookupToken())); } else { SelectExpression external; IEnumerable <ColumnExpression> externalColumns; if (!IsKey(currentSource, columns)) { Alias aliasDistinct = aliasGenerator.GetUniqueAlias(currentSource.Alias.Name + "D"); ColumnGenerator generatorDistinct = new ColumnGenerator(); List <ColumnDeclaration> columnDistinct = columns.Select(ce => generatorDistinct.MapColumn(ce)).ToList(); external = new SelectExpression(aliasDistinct, true, null, columnDistinct, currentSource, null, null, null, 0); Dictionary <ColumnExpression, ColumnExpression> distinctReplacements = columnDistinct.ToDictionary( cd => (ColumnExpression)cd.Expression, cd => cd.GetReference(aliasDistinct)); proj = (ProjectionExpression)ColumnReplacer.Replace(proj, distinctReplacements); externalColumns = distinctReplacements.Values.ToHashSet(); } else { external = currentSource; externalColumns = columns; } ColumnGenerator generatorSM = new ColumnGenerator(); List <ColumnDeclaration> columnsSMExternal = externalColumns.Select(ce => generatorSM.MapColumn(ce)).ToList(); List <ColumnDeclaration> columnsSMInternal = proj.Select.Columns.Select(cd => generatorSM.MapColumn(cd.GetReference(proj.Select.Alias))).ToList(); SelectExpression @internal = ExtractOrders(proj.Select, out List <OrderExpression>?innerOrders); Alias aliasSM = aliasGenerator.GetUniqueAlias(@internal.Alias.Name + "SM"); SelectExpression selectMany = new SelectExpression(aliasSM, false, null, columnsSMExternal.Concat(columnsSMInternal), new JoinExpression(JoinType.CrossApply, external, @internal, null), null, innerOrders, null, 0); SelectExpression old = currentSource; currentSource = WithoutOrder(selectMany); var selectManyReplacements = selectMany.Columns.ToDictionary( cd => (ColumnExpression)cd.Expression, cd => cd.GetReference(aliasSM)); Expression projector = ColumnReplacer.Replace(proj.Projector, selectManyReplacements); projector = Visit(projector); currentSource = old; Expression key = TupleReflection.TupleChainConstructor(columnsSMExternal.Select(cd => MakeEquatable(cd.GetReference(aliasSM)))); Type kvpType = typeof(KeyValuePair <,>).MakeGenericType(key.Type, projector.Type); ConstructorInfo ciKVP = kvpType.GetConstructor(new[] { key.Type, projector.Type }) !; Type projType = proj.UniqueFunction == null ? typeof(IEnumerable <>).MakeGenericType(kvpType) : kvpType; var childProj = new ProjectionExpression(selectMany, Expression.New(ciKVP, key, projector), proj.UniqueFunction, projType); return(new ChildProjectionExpression(childProj, TupleReflection.TupleChainConstructor(columns.Select(a => MakeEquatable(a))), inMList != null, inMList ?? proj.Type, new LookupToken())); } } }