/// <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)); }
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)); }
/// <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); }
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)); }
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); }
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)); }
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 )); } }
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)); }