상속: SqlExpressionVisitor
예제 #1
0
        /// <summary>
        /// Builds the lambda expression that will perform the projection
        /// </summary>
        /// <param name="dataAccessModel">The related data access model</param>
        /// <param name="sqlDatabaseContext">The related <see cref="SqlDatabaseContext"/></param>
        /// <param name="expression">
        /// The expression that performs the projection (can be any expression but usually is a MemberInit expression)
        /// </param>
        /// <returns>
        /// A <see cref="LambdaExpression"/> that takes two parameters: an <see cref="ObjectProjector"/>
        /// and an <see cref="IDataReader"/>.  The lambda expression will construct a single
        /// object for return from the current row in the given <see cref="IDataReader"/>.
        /// </returns>
        public static LambdaExpression Build(DataAccessModel dataAccessModel, SqlDatabaseContext sqlDatabaseContext, Expression expression, IEnumerable <string> columns)
        {
            var projectionBuilder = new ProjectionBuilder(dataAccessModel, sqlDatabaseContext, columns);

            var body = projectionBuilder.Visit(expression);

            return(Expression.Lambda(body, projectionBuilder.objectProjector, projectionBuilder.dataReader, projectionBuilder.dynamicParameters));
        }
예제 #2
0
        public static LambdaExpression Build(DataAccessModel dataAccessModel, SqlDatabaseContext sqlDatabaseContext, SqlQueryProvider queryProvider, Expression expression, ProjectionBuilderScope scope)
        {
            var projectionBuilder = new ProjectionBuilder(dataAccessModel, sqlDatabaseContext, queryProvider, scope);

            var body = projectionBuilder.Visit(expression);

            return(Expression.Lambda(body, projectionBuilder.objectProjector, projectionBuilder.dataReader, projectionBuilder.versionParameter, projectionBuilder.dynamicParameters));
        }
예제 #3
0
        /// <summary>
        /// Builds the lambda expression that will perform the projection
        /// </summary>
        /// <param name="dataAccessModel">The related data access model</param>
        /// <param name="sqlDatabaseContext">The related <see cref="SqlDatabaseContext"/></param>
        /// <param name="expression">
        /// The expression that performs the projection (can be any expression but usually is a MemberInit expression)
        /// </param>
        /// <returns>
        /// A <see cref="LambdaExpression"/> that takes two parameters: an <see cref="ObjectProjector"/>
        /// and an <see cref="IDataReader"/>.  The lambda expression will construct a single
        /// object for return from the current row in the given <see cref="IDataReader"/>.
        /// </returns>
        public static LambdaExpression Build(DataAccessModel dataAccessModel, SqlDatabaseContext sqlDatabaseContext, Expression expression, IEnumerable<string> columns)
        {
            var projectionBuilder = new ProjectionBuilder(dataAccessModel, sqlDatabaseContext, columns);

            var body = projectionBuilder.Visit(expression);

            return Expression.Lambda(body, projectionBuilder.objectProjector, projectionBuilder.dataReader, projectionBuilder.dynamicParameters);
        }
예제 #4
0
        public static LambdaExpression Build(DataAccessModel dataAccessModel, SqlDatabaseContext sqlDatabaseContext, SqlQueryProvider queryProvider, Expression expression, ProjectionBuilderScope scope, out Expression <Func <IDataReader, object[]> > rootKeys)
        {
            var projectionBuilder = new ProjectionBuilder(dataAccessModel, sqlDatabaseContext, queryProvider, scope);

            var body = projectionBuilder.Visit(expression);

            if (projectionBuilder.scope.rootPrimaryKeys.Count > 0)
            {
                rootKeys = Expression.Lambda <Func <IDataReader, object[]> >(Expression.NewArrayInit(typeof(object), projectionBuilder.scope.rootPrimaryKeys), projectionBuilder.dataReader);
            }
            else
            {
                rootKeys = null;
            }

            return(Expression.Lambda(typeof(ObjectReaderFunc <>).MakeGenericType(body.Type), body, projectionBuilder.objectProjector, projectionBuilder.dataReader, projectionBuilder.versionParameter, projectionBuilder.dynamicParameters));
        }
예제 #5
0
		public static LambdaExpression Build(DataAccessModel dataAccessModel, SqlDatabaseContext sqlDatabaseContext, SqlQueryProvider queryProvider, Expression expression, ProjectionBuilderScope scope, out Expression<Func<IDataReader, object[]>> rootKeys)
		{
			var projectionBuilder = new ProjectionBuilder(dataAccessModel, sqlDatabaseContext, queryProvider, scope);

			var body = projectionBuilder.Visit(expression);

			if (projectionBuilder.scope.rootPrimaryKeys.Count > 0)
			{
				rootKeys = Expression.Lambda<Func<IDataReader, object[]>>(Expression.NewArrayInit(typeof(object), projectionBuilder.scope.rootPrimaryKeys), projectionBuilder.dataReader);
			}
			else
			{
				rootKeys = null;
			}

			return Expression.Lambda(body, projectionBuilder.objectProjector, projectionBuilder.dataReader, projectionBuilder.versionParameter, projectionBuilder.dynamicParameters, projectionBuilder.filterParameter);
		}
예제 #6
0
        internal ExecutionBuildResult BuildExecution(Expression expression, LambdaExpression projection = null, object[] placeholderValues = null)
        {
            ProjectorExpressionCacheInfo cacheInfo;
            var skipFormatResultSubstitution = false;
            var projectionExpression         = expression as SqlProjectionExpression ?? (SqlProjectionExpression)Bind(this.DataAccessModel, this.SqlDatabaseContext.SqlDataTypeProvider, expression);

            var key = new ExpressionCacheKey(projectionExpression, projection);

            if (!this.SqlDatabaseContext.projectionExpressionCache.TryGetValue(key, out cacheInfo))
            {
                if (expression != projectionExpression)
                {
                    placeholderValues            = SqlConstantPlaceholderValuesCollector.CollectValues(projectionExpression);
                    projectionExpression         = (SqlProjectionExpression)Optimize(this.DataAccessModel, this.SqlDatabaseContext, projectionExpression);
                    skipFormatResultSubstitution = true;
                }

                var oldCache     = this.SqlDatabaseContext.projectionExpressionCache;
                var formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(projectionExpression);

                SqlQueryFormatResult formatResultForCache = null;

                if (formatResult.Cacheable)
                {
                    var parameters = formatResult.ParameterValues.ToList();

                    foreach (var index in formatResult.ParameterIndexToPlaceholderIndexes.Keys)
                    {
                        var value = parameters[index];

                        parameters[index] = value.ChangeValue(value.Type.GetDefaultValue());
                    }

                    formatResultForCache = formatResult.ChangeParameterValues(parameters);
                }
                else
                {
                    if (!skipFormatResultSubstitution)
                    {
                        // Edge case where inner projection from ProjectionBuilder can't be cached (related DeflatedPredicated with a complex predicate)

                        skipFormatResultSubstitution = true;

                        projectionExpression = (SqlProjectionExpression)SqlConstantPlaceholderReplacer.Replace(projectionExpression, placeholderValues);

                        formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(projectionExpression);
                    }
                }

                cacheInfo = new ProjectorExpressionCacheInfo(projectionExpression, formatResultForCache);

                if (projection == null)
                {
                    var columns = projectionExpression.Select.Columns.Select(c => c.Name);

                    projection = ProjectionBuilder.Build(this.DataAccessModel, this.SqlDatabaseContext, this, projectionExpression.Projector, new ProjectionBuilderScope(columns.ToArray()));
                }

                BuildProjector(projection, projectionExpression.Aggregator, out cacheInfo.projector, out cacheInfo.asyncProjector);

                if (this.SqlDatabaseContext.projectionExpressionCache.Count >= ProjectorCacheMaxLimit)
                {
                    ProjectionExpressionCacheLogger.Debug(() => $"ProjectionExpressionCache has been flushed because it overflowed with a size of {ProjectionExpressionCacheMaxLimit}\n\nProjectionExpression: {projectionExpression}\n\nAt: {new StackTrace()}");

                    var newCache = new Dictionary <ExpressionCacheKey, ProjectorExpressionCacheInfo>(ProjectorCacheMaxLimit, ExpressionCacheKeyEqualityComparer.Default);

                    foreach (var value in oldCache.Take(oldCache.Count / 3))
                    {
                        newCache[value.Key] = value.Value;
                    }

                    newCache[key] = cacheInfo;

                    this.SqlDatabaseContext.projectionExpressionCache = newCache;
                }
                else
                {
                    var newCache = new Dictionary <ExpressionCacheKey, ProjectorExpressionCacheInfo>(oldCache, ExpressionCacheKeyEqualityComparer.Default)
                    {
                        [key] = cacheInfo
                    };

                    this.SqlDatabaseContext.projectionExpressionCache = newCache;
                }

                ProjectionCacheLogger.Debug(() => $"Cached projection for query:\n{GetQueryText(formatResult)}\n\nProjector:\n{cacheInfo.projector}");
                ProjectionCacheLogger.Debug(() => $"Projector Cache Size: {this.SqlDatabaseContext.projectionExpressionCache.Count}");

                cacheInfo.formatResult = formatResult;
            }
            else
            {
                ProjectionCacheLogger.Debug(() => $"Cache hit for query:\n{GetQueryText(cacheInfo.formatResult)}");
            }

            if (placeholderValues == null)
            {
                placeholderValues = SqlConstantPlaceholderValuesCollector.CollectValues(projectionExpression);
            }

            if (cacheInfo.formatResult == null)
            {
                var projector          = SqlConstantPlaceholderReplacer.Replace(cacheInfo.projectionExpression, placeholderValues);
                var optimizedProjector = Optimize(this.DataAccessModel, this.SqlDatabaseContext, projector);

                cacheInfo.formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(optimizedProjector);
            }
            else if (!skipFormatResultSubstitution)
            {
                var parameters = cacheInfo.formatResult.ParameterValues.ToList();

                foreach (var indexes in cacheInfo.formatResult.ParameterIndexToPlaceholderIndexes)
                {
                    var index            = indexes.Key;
                    var placeholderIndex = indexes.Value;

                    parameters[index] = parameters[index].ChangeValue(placeholderValues[placeholderIndex]);
                }

                cacheInfo.formatResult = cacheInfo.formatResult.ChangeParameterValues(parameters);
            }

            return(new ExecutionBuildResult(this, cacheInfo.formatResult, cacheInfo.projector, cacheInfo.asyncProjector, placeholderValues));
        }
예제 #7
0
        private PrivateExecuteResult <T> PrivateExecute <T>(Expression expression)
        {
            var projectionExpression = expression as SqlProjectionExpression;

            if (projectionExpression == null)
            {
                expression = Evaluator.PartialEval(expression);

                if (this.RelatedDataAccessObjectContext == null)
                {
                    expression = QueryBinder.Bind(this.DataAccessModel, expression, null, null);
                }
                else
                {
                    expression = QueryBinder.Bind(this.DataAccessModel, expression, this.RelatedDataAccessObjectContext.ElementType, this.RelatedDataAccessObjectContext.ExtraCondition);
                }

                projectionExpression = (SqlProjectionExpression)Optimize(expression, this.SqlDatabaseContext.SqlDataTypeProvider.GetTypeForEnums(), true);
            }

            ProjectorCacheInfo cacheInfo;

            var columns = projectionExpression.Select.Columns.Select(c => c.Name);

            var formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(projectionExpression, SqlQueryFormatterOptions.Default);

            var placeholderValues = PlaceholderValuesCollector.CollectValues(expression);

            var key = new ProjectorCacheKey(projectionExpression, this.SqlDatabaseContext);

            var projectorCache = this.SqlDatabaseContext.projectorCache;

            if (!projectorCache.TryGetValue(key, out cacheInfo))
            {
                var projectionLambda = ProjectionBuilder.Build(this.DataAccessModel, this.SqlDatabaseContext, projectionExpression.Projector, columns);

                cacheInfo.elementType = projectionLambda.Body.Type;
                cacheInfo.projector   = projectionLambda.Compile();

                var aggregates = AggregateFinder.Find(projectionExpression);

                if (aggregates.Count == 1)
                {
                    cacheInfo.sqlAggregateType = aggregates.First().AggregateType;
                }

                var newCache = new Dictionary <ProjectorCacheKey, ProjectorCacheInfo>(projectorCache, ProjectorCacheEqualityComparer.Default);

                if (!projectorCache.ContainsKey(key))
                {
                    newCache[key] = cacheInfo;
                }

                this.SqlDatabaseContext.projectorCache = newCache;
            }

            var elementType         = TypeHelper.GetElementType(cacheInfo.elementType);
            var concreteElementType = elementType;

            if (elementType.IsDataAccessObjectType())
            {
                Type type;
                TypeHelper.GetElementType(cacheInfo.elementType);
                elementType         = this.DataAccessModel.GetDefinitionTypeFromConcreteType(elementType);
                concreteElementType = this.DataAccessModel.GetConcreteTypeFromDefinitionType(elementType);

                if (this.RelatedDataAccessObjectContext == null)
                {
                    type = typeof(DataAccessObjectProjector <,>);
                }
                else
                {
                    type = typeof(RelatedDataAccessObjectProjector <,>);
                }

                return(new PrivateExecuteResult <T>
                       (
                           (IEnumerable <T>)Activator.CreateInstance
                           (
                               type.MakeGenericType(elementType, concreteElementType),
                               this,
                               this.DataAccessModel,
                               formatResult,
                               this.SqlDatabaseContext,
                               cacheInfo.projector,
                               this.RelatedDataAccessObjectContext,
                               projectionExpression.SelectFirstType,
                               cacheInfo.sqlAggregateType,
                               projectionExpression.IsDefaultIfEmpty,
                               placeholderValues
                           ),
                           projectionExpression.SelectFirstType,
                           cacheInfo.sqlAggregateType,
                           projectionExpression.IsDefaultIfEmpty,
                           projectionExpression.DefaultValueExpression
                       ));
            }
            else
            {
                return(new PrivateExecuteResult <T>
                       (
                           (IEnumerable <T>)Activator.CreateInstance
                           (
                               typeof(ObjectProjector <,>).MakeGenericType(elementType, concreteElementType),
                               this,
                               this.DataAccessModel,
                               formatResult,
                               this.SqlDatabaseContext,
                               cacheInfo.projector,
                               this.RelatedDataAccessObjectContext,
                               projectionExpression.SelectFirstType,
                               cacheInfo.sqlAggregateType,
                               projectionExpression.IsDefaultIfEmpty,
                               placeholderValues
                           ),
                           projectionExpression.SelectFirstType,
                           cacheInfo.sqlAggregateType,
                           projectionExpression.IsDefaultIfEmpty,
                           projectionExpression.DefaultValueExpression
                       ));
            }
        }
예제 #8
0
        internal ExecutionBuildResult BuildExecution(Expression expression, LambdaExpression projection = null, object[] placeholderValues = null, Expression <Func <IDataReader, object[]> > rootKeys = null)
        {
            var skipFormatResultSubstitution = false;
            var projectionExpression         = expression as SqlProjectionExpression ?? (SqlProjectionExpression)Bind(this.DataAccessModel, this.SqlDatabaseContext.SqlDataTypeProvider, expression);

            var foundCachedProjection = false;
            var key = new ExpressionCacheKey(projectionExpression, projection);

            if (!this.SqlDatabaseContext.projectionExpressionCache.TryGetValue(key, out var cacheInfo))
            {
                if (expression != projectionExpression)
                {
                    placeholderValues            = SqlConstantPlaceholderValuesCollector.CollectValues(projectionExpression);
                    projectionExpression         = (SqlProjectionExpression)Optimize(this.DataAccessModel, projectionExpression);
                    skipFormatResultSubstitution = true;
                }

                var formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(projectionExpression, SqlQueryFormatterOptions.Default);

                SqlQueryFormatResult formatResultForCache = null;

                if (formatResult.Cacheable)
                {
                    var parameters = formatResult.ParameterValues.ToList();

                    foreach (var index in formatResult.ParameterIndexToPlaceholderIndexes.Keys)
                    {
                        var value = parameters[index];

                        parameters[index] = value.ChangeValue(value.Type.GetDefaultValue());
                    }

                    formatResultForCache = formatResult.ChangeParameterValues(parameters);
                }
                else
                {
                    if (!skipFormatResultSubstitution)
                    {
                        // Edge case where inner projection from ProjectionBuilder can't be cached (related DeflatedPredicated with a complex predicate)

                        skipFormatResultSubstitution = true;

                        projectionExpression = (SqlProjectionExpression)SqlConstantPlaceholderReplacer.Replace(projectionExpression, placeholderValues);

                        formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(projectionExpression);
                    }
                }

                cacheInfo = new ProjectorExpressionCacheInfo(projectionExpression, formatResultForCache);

                var columns = projectionExpression.Select.Columns.Select(c => c.Name).ToArray();

                if (projection == null)
                {
                    projection = ProjectionBuilder.Build(this.DataAccessModel, this.SqlDatabaseContext, this, projectionExpression.Projector, new ProjectionBuilderScope(columns), out rootKeys);
                }

                this.BuildProjector(projection, projectionExpression.Aggregator, rootKeys, out cacheInfo.projector, out cacheInfo.asyncProjector);

                this.SqlDatabaseContext.projectionExpressionCache = this.SqlDatabaseContext.projectionExpressionCache.Clone(key, cacheInfo, "ProjectionExpression", this.ProjectionExpressionCacheMaxLimit, ProjectionCacheLogger, c => c.projectionExpression.ToString());

                ProjectionCacheLogger.Debug(() => $"Cached projection for query:\n{this.GetQueryText(formatResult, this.GetParamName)}\n\nProjector:\n{cacheInfo.projector}");
                ProjectionCacheLogger.Debug(() => $"Projector Cache Size: {this.SqlDatabaseContext.projectionExpressionCache.Count}");

                cacheInfo.formatResult = formatResult;
            }
            else
            {
                foundCachedProjection = true;
            }

            if (placeholderValues == null)
            {
                placeholderValues = SqlConstantPlaceholderValuesCollector.CollectValues(projectionExpression);
            }

            if (cacheInfo.formatResult == null)
            {
                var projector          = SqlConstantPlaceholderReplacer.Replace(cacheInfo.projectionExpression, placeholderValues);
                var optimizedProjector = Optimize(this.DataAccessModel, projector);

                cacheInfo.formatResult = this.SqlDatabaseContext.SqlQueryFormatterManager.Format(optimizedProjector);
            }
            else if (!skipFormatResultSubstitution)
            {
                var parameters = cacheInfo.formatResult.ParameterValues.ToList();

                foreach (var indexes in cacheInfo.formatResult.ParameterIndexToPlaceholderIndexes)
                {
                    var index            = indexes.Key;
                    var placeholderIndex = indexes.Value;

                    parameters[index] = parameters[index].ChangeValue(placeholderValues[placeholderIndex]);
                }

                cacheInfo.formatResult = cacheInfo.formatResult.ChangeParameterValues(parameters);
            }

            if (foundCachedProjection)
            {
                ProjectionCacheLogger.Debug(() => $"Cache hit for query:\n{this.GetQueryText(cacheInfo.formatResult, this.GetParamName)}");
            }

            return(new ExecutionBuildResult(this, cacheInfo.formatResult, cacheInfo.projector, cacheInfo.asyncProjector, placeholderValues));
        }