private static void GetIncludeExcludes <T>(IList <Expression> expressions, IncludeExcludeParser parser, FetchNode rootNode, bool isInclude /* as opposed to exclude */) where T : class, new() { if (expressions == null) { return; } foreach (var expression in expressions) { parser.ParseExpression <T>(expression, rootNode, isInclude); } }
private static void GetIncludeExcludes <T>( IList <Expression> expressions, ref IDictionary <Type, IList <IColumn> > includes, IncludeExcludeParser parser) where T : class, new() { if (expressions != null) { includes = new Dictionary <Type, IList <IColumn> >(); foreach (var expression in expressions) { var column = parser.ParseExpression(expression); if (!includes.ContainsKey(column.Map.Type)) { includes.Add(column.Map.Type, new List <IColumn>()); } includes[column.Map.Type].Add(column); } } }
public SelectWriterResult GenerateSql <T>(SelectQuery <T> selectQuery, bool enforceAlias = false) where T : class, new() { // TODO: one StringBuilder to rule them all - Good luck with that ;-) (insertions are expensive) var sql = new StringBuilder(); DynamicParameters parameters = new DynamicParameters(); // get fetch tree structure int aliasCounter; int numberCollectionFetches; var rootNode = this.fetchTreeParser.GetFetchTree(selectQuery, out aliasCounter, out numberCollectionFetches); IDictionary <Type, IList <IColumn> > includes = null; IDictionary <Type, IList <IColumn> > excludes = null; if ((selectQuery.Includes != null && selectQuery.Includes.Any()) || (selectQuery.Excludes != null && selectQuery.Excludes.Any())) { var parser = new IncludeExcludeParser(this.Configuration); GetIncludeExcludes <T>(selectQuery.Includes, ref includes, parser); GetIncludeExcludes <T>(selectQuery.Excludes, ref excludes, parser); } if (numberCollectionFetches > 0) { if (numberCollectionFetches > 1 && (rootNode.Children.Count(c => c.Value.Column.Relationship == RelationshipType.OneToMany || c.Value.ContainedCollectionfetchesCount > 0) > 1)) { // multiple one to many branches so we'll perform a union query if (selectQuery.TakeN > 0 || selectQuery.SkipN > 0) { // TODO this is temporary, should generate union query similar to next rootNode = this.GeneratePagingCollectionSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, includes, excludes, ref parameters); } else { rootNode = this.GenerateNoPagingUnionSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, includes, excludes, ref parameters); } } else { if (selectQuery.TakeN > 0 || selectQuery.SkipN > 0) { // we're sub-selecting so need to use a subquery rootNode = this.GeneratePagingCollectionSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, includes, excludes, ref parameters); } else { // we're fetching all things rootNode = this.GenerateNoPagingSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, includes, excludes, ref parameters); } } } else { // no collection fetches rootNode = this.GenerateNoPagingSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, includes, excludes, ref parameters); } return(new SelectWriterResult(sql.ToString(), parameters, rootNode) { NumberCollectionsFetched = numberCollectionFetches }); }
private SelectWriterResult InnerGenerateSql <T>(SelectQuery <T> selectQuery, AutoNamingDynamicParameters parameters, bool enforceAlias, FetchNode rootNode, int numberCollectionFetches, StringBuilder sql, bool isProjectedQuery) where T : class, new() // add in any includes or excludes { if ((selectQuery.Includes != null && selectQuery.Includes.Any()) || (selectQuery.Excludes != null && selectQuery.Excludes.Any())) { var parser = new IncludeExcludeParser(this.Configuration); rootNode = rootNode ?? new FetchNode(); GetIncludeExcludes <T>(selectQuery.Includes, parser, rootNode, true); GetIncludeExcludes <T>(selectQuery.Excludes, parser, rootNode, false); } if (numberCollectionFetches > 0) { if (numberCollectionFetches > 1 && (rootNode.Children.Count(c => c.Value.Column.Relationship == RelationshipType.OneToMany || c.Value.ContainedCollectionfetchesCount > 0) > 1)) { // multiple one to many branches so we'll perform a union query if (selectQuery.TakeN > 0 || selectQuery.SkipN > 0) { // TODO this is temporary, should generate union query similar to next rootNode = this.GeneratePagingCollectionSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, parameters, isProjectedQuery); } else { rootNode = this.GenerateNoPagingUnionSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, parameters, isProjectedQuery); } } else { if (selectQuery.TakeN > 0 || selectQuery.SkipN > 0) { // we're sub-selecting so need to use a subquery rootNode = this.GeneratePagingCollectionSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, parameters, isProjectedQuery); } else { // we're fetching all things rootNode = this.GenerateNoPagingSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, parameters, isProjectedQuery); } } } else { // no collection fetches // see if we can transform to union query for non-root disjunctions var nonRootDisjunctionTransformationSucceeded = false; if (selectQuery.TakeN == 0 && selectQuery.SkipN == 0) { var outerJoinDisjunctionTransformer = new OuterJoinDisjunctionTransformer(this.Configuration); int substitutedWhereClauseIndex = -1; Expression <Func <T, bool> > substitutedWhereClause = null; IEnumerable <Expression <Func <T, bool> > > substitutions = null; foreach (var whereClauseEntry in selectQuery.WhereClauses.AsSmartEnumerable()) { var whereClause = whereClauseEntry.Value; var result = outerJoinDisjunctionTransformer.AttemptGetOuterJoinDisjunctions(whereClause); if (result.ContainsOuterJoinDisjunction) { if (substitutedWhereClause != null) { // we'll bail out here as we're not supporting multiple disjunctions substitutedWhereClause = null; break; } substitutedWhereClauseIndex = whereClauseEntry.Index; substitutedWhereClause = whereClause; substitutions = result.UnionWhereClauses; } } if (substitutedWhereClause != null) { // we don't want to order the unioned queries, we'll order them subsequently var originalOrderClauses = selectQuery.OrderClauses; selectQuery.OrderClauses = new Queue <OrderClause <T> >(); // we need to copy the fetch node and re-use it inside every query var originalRootNode = rootNode != null ? rootNode.Clone() : new FetchNode(); // we force the unions to have the same alias foreach (var substitution in substitutions.AsSmartEnumerable()) { // swap out the original where clause for the substitute selectQuery.WhereClauses.RemoveAt(substitutedWhereClauseIndex); selectQuery.WhereClauses.Insert(substitutedWhereClauseIndex, substitution.Value); var substitutionRootNode = originalRootNode.Clone(); rootNode = this.GenerateNoPagingSql(selectQuery, enforceAlias, substitutionRootNode, sql, numberCollectionFetches, parameters, isProjectedQuery); if (!substitution.IsLast) { sql.Append(" union "); } } if (originalOrderClauses != null && originalOrderClauses.Any()) { this.AddOrderByClause(originalOrderClauses, sql, rootNode); } nonRootDisjunctionTransformationSucceeded = true; } } if (!nonRootDisjunctionTransformationSucceeded) { rootNode = this.GenerateNoPagingSql(selectQuery, enforceAlias, rootNode, sql, numberCollectionFetches, parameters, isProjectedQuery); } } return(new SelectWriterResult(sql.ToString(), parameters, rootNode) { NumberCollectionsFetched = numberCollectionFetches }); }