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