Esempio n. 1
0
        private void BuildProjector(LambdaExpression projectionLambda, LambdaExpression aggregator, out Delegate projector, out Delegate asyncProjector)
        {
            var sqlQueryProviderParam  = Expression.Parameter(typeof(SqlQueryProvider));
            var formatResultsParam     = Expression.Parameter(typeof(SqlQueryFormatResult));
            var placeholderValuesParam = Expression.Parameter(typeof(object[]));

            var elementType = projectionLambda.ReturnType;

            Expression executor;

            if (elementType.IsDataAccessObjectType())
            {
                var concreteElementType = this.DataAccessModel.GetConcreteTypeFromDefinitionType(elementType);

                var constructor = typeof(DataAccessObjectProjector <,>).MakeGenericType(elementType, concreteElementType).GetConstructors(BindingFlags.Public | BindingFlags.Instance).Single();

                executor = Expression.New
                           (
                    constructor,
                    sqlQueryProviderParam,
                    Expression.Constant(this.DataAccessModel),
                    Expression.Constant(this.SqlDatabaseContext),
                    formatResultsParam,
                    placeholderValuesParam,
                    projectionLambda
                           );
            }
            else
            {
                if ((aggregator?.Body as MethodCallExpression)?.Method.GetGenericMethodOrRegular() == MethodInfoFastRef.EnumerableExtensionsAlwaysReadFirstMethod)
                {
                    var constructor = typeof(AlwaysReadFirstObjectProjector <,>).MakeGenericType(projectionLambda.ReturnType, projectionLambda.ReturnType).GetConstructors(BindingFlags.Public | BindingFlags.Instance).Single();

                    executor = Expression.New
                               (
                        constructor,
                        sqlQueryProviderParam,
                        Expression.Constant(this.DataAccessModel),
                        Expression.Constant(this.SqlDatabaseContext),
                        formatResultsParam,
                        placeholderValuesParam,
                        projectionLambda
                               );
                }
                else
                {
                    var projectorType = !DataAccessObjectAwareResultTypeComparerBuilder.NeedsComparer(projectionLambda.ReturnType) ? typeof(ObjectProjector <,>) : typeof(DataAccessObjectContainerProjector <,>);

                    var constructor = projectorType.MakeGenericType(projectionLambda.ReturnType, projectionLambda.ReturnType).GetConstructors(BindingFlags.Public | BindingFlags.Instance).Single();

                    executor = Expression.New
                               (
                        constructor,
                        sqlQueryProviderParam,
                        Expression.Constant(this.DataAccessModel),
                        Expression.Constant(this.SqlDatabaseContext),
                        formatResultsParam,
                        placeholderValuesParam,
                        projectionLambda
                               );
                }
            }

            var asyncExecutor     = executor;
            var cancellationToken = Expression.Parameter(typeof(CancellationToken));

            if (aggregator != null)
            {
                var originalExecutor = executor;
                var aggr             = aggregator;
                var newBody          = SqlConstantPlaceholderReplacer.Replace(aggr.Body, placeholderValuesParam);
                executor = SqlExpressionReplacer.Replace(newBody, aggr.Parameters[0], originalExecutor);

                newBody       = ProjectionAsyncRewriter.Rewrite(newBody, cancellationToken);
                asyncExecutor = SqlExpressionReplacer.Replace(newBody, aggr.Parameters[0], originalExecutor);
            }

            projectionLambda = Expression.Lambda(executor, sqlQueryProviderParam, formatResultsParam, placeholderValuesParam);
            var asyncProjectorLambda = Expression.Lambda(asyncExecutor, sqlQueryProviderParam, formatResultsParam, placeholderValuesParam, cancellationToken);

            ProjectorCacheInfo cacheInfo;
            var key      = new ProjectorCacheKey(projectionLambda);
            var oldCache = this.SqlDatabaseContext.projectorCache;

            if (!oldCache.TryGetValue(key, out cacheInfo))
            {
                cacheInfo.projector      = projectionLambda.Compile();
                cacheInfo.asyncProjector = asyncProjectorLambda.Compile();

                if (this.SqlDatabaseContext.projectorCache.Count >= ProjectorCacheMaxLimit)
                {
                    ProjectionExpressionCacheLogger.Info(() => $"Projector has been flushed because it overflowed with a size of {ProjectionExpressionCacheMaxLimit}\n\nProjector: {projectionLambda}\n\nAt: {new StackTrace()}");

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

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

                    newCache[key] = cacheInfo;

                    this.SqlDatabaseContext.projectorCache = newCache;
                }
                else
                {
                    var newCache = new Dictionary <ProjectorCacheKey, ProjectorCacheInfo>(oldCache, ProjectorCacheEqualityComparer.Default)
                    {
                        [key] = cacheInfo
                    };

                    this.SqlDatabaseContext.projectorCache = newCache;
                }

                ProjectionCacheLogger.Info(() => $"Cached projector:\n{cacheInfo.projector}");
                ProjectionCacheLogger.Debug(() => $"Projector Cache Size: {this.SqlDatabaseContext.projectionExpressionCache.Count}");
            }

            projector      = cacheInfo.projector;
            asyncProjector = cacheInfo.asyncProjector;
        }
Esempio n. 2
0
        private void BuildProjector(LambdaExpression projectionLambda, LambdaExpression aggregator, Expression <Func <IDataReader, object[]> > keyBuilder, out Delegate projector, out Delegate asyncProjector)
        {
            var sqlQueryProviderParam  = Expression.Parameter(typeof(SqlQueryProvider));
            var formatResultsParam     = Expression.Parameter(typeof(SqlQueryFormatResult));
            var placeholderValuesParam = Expression.Parameter(typeof(object[]));

            var elementType = projectionLambda.ReturnType;

            Expression executor;

            if (elementType.IsDataAccessObjectType())
            {
                var concreteElementType = this.DataAccessModel.GetConcreteTypeFromDefinitionType(elementType);

                var constructor = typeof(DataAccessObjectProjector <,>)
                                  .MakeGenericType(elementType, concreteElementType)
                                  .GetConstructors(BindingFlags.Public | BindingFlags.Instance).Single();

                executor = Expression.New
                           (
                    constructor,
                    sqlQueryProviderParam,
                    Expression.Constant(this.DataAccessModel),
                    Expression.Constant(this.SqlDatabaseContext),
                    formatResultsParam,
                    placeholderValuesParam,
                    projectionLambda
                           );
            }
            else
            {
                if ((aggregator?.Body as MethodCallExpression)?.Method.GetGenericMethodOrRegular() == MethodInfoFastRef.EnumerableExtensionsAlwaysReadFirstMethod)
                {
                    var constructor = typeof(AlwaysReadFirstObjectProjector <,>).MakeGenericType(projectionLambda.ReturnType, projectionLambda.ReturnType).GetConstructors(BindingFlags.Public | BindingFlags.Instance).Single();

                    executor = Expression.New
                               (
                        constructor,
                        sqlQueryProviderParam,
                        Expression.Constant(this.DataAccessModel),
                        Expression.Constant(this.SqlDatabaseContext),
                        formatResultsParam,
                        placeholderValuesParam,
                        projectionLambda
                               );
                }
                else
                {
                    if (keyBuilder == null)
                    {
                        var constructor = typeof(ObjectProjector <,>)
                                          .MakeGenericType(projectionLambda.ReturnType, projectionLambda.ReturnType)
                                          .GetConstructors(BindingFlags.Public | BindingFlags.Instance)
                                          .Single();

                        executor = Expression.New
                                   (
                            constructor,
                            sqlQueryProviderParam,
                            Expression.Constant(this.DataAccessModel),
                            Expression.Constant(this.SqlDatabaseContext),
                            formatResultsParam,
                            placeholderValuesParam,
                            projectionLambda
                                   );
                    }
                    else
                    {
                        var constructor = typeof(ComplexDataAccessObjectProjector <,>)
                                          .MakeGenericType(projectionLambda.ReturnType, projectionLambda.ReturnType)
                                          .GetConstructors(BindingFlags.Public | BindingFlags.Instance)
                                          .Single();

                        executor = Expression.New
                                   (
                            constructor,
                            sqlQueryProviderParam,
                            Expression.Constant(this.DataAccessModel),
                            Expression.Constant(this.SqlDatabaseContext),
                            formatResultsParam,
                            placeholderValuesParam,
                            projectionLambda,
                            keyBuilder
                                   );
                    }
                }
            }

            var asyncExecutor     = executor;
            var cancellationToken = Expression.Parameter(typeof(CancellationToken));

            if (aggregator != null)
            {
                var originalExecutor = executor;
                var aggr             = aggregator;
                var newBody          = SqlConstantPlaceholderReplacer.Replace(aggr.Body, placeholderValuesParam);
                executor = SqlExpressionReplacer.Replace(newBody, aggr.Parameters[0], originalExecutor);

                newBody       = ProjectionAsyncRewriter.Rewrite(newBody, cancellationToken);
                asyncExecutor = SqlExpressionReplacer.Replace(newBody, aggr.Parameters[0], originalExecutor);
            }

            projectionLambda = Expression.Lambda(executor, sqlQueryProviderParam, formatResultsParam, placeholderValuesParam);
            var asyncProjectorLambda = Expression.Lambda(asyncExecutor, sqlQueryProviderParam, formatResultsParam, placeholderValuesParam, cancellationToken);

            var key      = new ProjectorCacheKey(projectionLambda);
            var oldCache = this.SqlDatabaseContext.projectorCache;

            if (!oldCache.TryGetValue(key, out var cacheInfo))
            {
                cacheInfo.projector      = projectionLambda.Compile();
                cacheInfo.asyncProjector = asyncProjectorLambda.Compile();

                this.SqlDatabaseContext.projectorCache = oldCache.Clone(key, cacheInfo, "projectorCache", this.ProjectorCacheMaxLimit);

                ProjectionCacheLogger.Info(() => $"Cached projector:\n{projectionLambda}");
                ProjectionCacheLogger.Debug(() => $"Projector Cache Size: {this.SqlDatabaseContext.projectionExpressionCache.Count}");
            }

            projector      = cacheInfo.projector;
            asyncProjector = cacheInfo.asyncProjector;
        }
Esempio n. 3
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));
        }
Esempio n. 4
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));
        }