예제 #1
0
        public LambdaExpression GetPropertyExpression(Type actualType, string propertyName,
                                                      Type exptectedParamType = null, Type exptectedReturnType = null, bool ignoreCase = true)
        {
            actualType.CheckArgumentNull(nameof(actualType));

            if (exptectedParamType == null)
            {
                exptectedParamType = actualType;
            }

            ExpressionCacheKey cacheKey = new ExpressionCacheKey(0,
                                                                 HashCode.Combine(actualType, exptectedParamType, exptectedReturnType),
                                                                 propertyName.ToLowerInvariant().GetHashCode());

            return((LambdaExpression)ExpressionsCache.GetOrAdd(cacheKey, _ =>
            {
                ParameterExpression parameter = Expression.Parameter(exptectedParamType);
                Expression finalExpression = parameter;

                if (actualType != exptectedParamType)
                {
                    finalExpression = finalExpression.Convert(actualType);
                }

                finalExpression =
                    GetPropertyAccessExpression(finalExpression, propertyName, exptectedReturnType, ignoreCase);

                return finalExpression
                .Lambda(new[] { parameter });
            }));
        }
예제 #2
0
        public object Eval(string code, IDictionary <string, object> vars)
        {
            var paramsList = new List <ParameterExpression>();
            var valuesList = new List <object>();

            if (ExposeVars)
            {
                foreach (var varEntry in vars)
                {
                    paramsList.Add(Expression.Parameter(varEntry.Value != null ? varEntry.Value.GetType() : typeof(object), varEntry.Key));
                    valuesList.Add(varEntry.Value);
                }
            }
            else
            {
                paramsList.Add(Expression.Parameter(typeof(IDictionary <string, object>), VarParamName));
                valuesList.Add(vars);
            }
            Delegate compiledExpr;
            // check in cache
            ExpressionCacheKey cacheKey = new ExpressionCacheKey(code, paramsList.ToArray());

            if (!CacheEnabled || !Cache.TryGetValue(cacheKey, out compiledExpr))
            {
                var expression = DynamicExpression.ParseLambda(paramsList.ToArray(), typeof(object), code, null);
                compiledExpr = expression.Compile();
                if (CacheEnabled)
                {
                    Cache[cacheKey] = compiledExpr;
                }
            }
            return(compiledExpr.DynamicInvoke(valuesList.ToArray()));
        }
예제 #3
0
        /// <summary>
        ///     Returns the function that evaluates to the value of the specified expression,
        ///     returning the default value of the result type (<c>null</c> for reference types)
        ///     instead of throwing an exception when one or more of the referenced members in the
        ///     expression are <c>null</c>.
        /// </summary>
        /// <typeparam name="TSource">The type of the instance to evaluate the expression on.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="expression">The expression to evaluate.</param>
        /// <returns>The function that represents the expression or the default value of the result type, if one or more of the referenced members in the expression are <c>null</c>.</returns>
        /// <exception cref="ArgumentNullException">The specified <paramref name="expression"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">The specified <paramref name="expression"/> does not appear to express a value.</exception>
        /// <remarks>
        ///     When calling this method multiple times in the same AppDomain and providing the
        ///     same expression, a cached instance of the delegate will be returned.
        /// </remarks>
        public static Func <TSource, TResult> GetSafeGetDelegate <TSource, TResult>(Expression <Func <TSource, TResult> > expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            Func <TSource, TResult> function;

            lock (expressionCacheLock)
            {
                ExpressionCacheKey key = new ExpressionCacheKey()
                {
                    Expression   = expression.ToString(),
                    InstanceType = typeof(TSource)
                };

                if (!expressionCache.ContainsKey(key))
                {
                    MemberExpression body = GetBody <TSource, TResult>(expression);

                    if (body == null)
                    {
                        throw new ArgumentException("Does not appear to express a value.", "expression");
                    }

                    IList <MemberExpression> reversedSourceFragments = GetFragments(body);

                    bool canHaveNulls = reversedSourceFragments.Any(n => n.Type.IsClass);

                    if (!canHaveNulls)
                    {
                        function = expression.Compile();
                    }
                    else
                    {
                        function = CreateSafeGetDelegate <TSource, TResult>(reversedSourceFragments);
                    }

                    expressionCache[key] = function;
                }
                else
                {
                    function = (Func <TSource, TResult>)expressionCache[key];
                }
            }

            return(function);
        }
예제 #4
0
            public override bool Equals(object obj)
            {
                ExpressionCacheKey key = (ExpressionCacheKey)obj;

                if (Code != key.Code)
                {
                    return(false);
                }
                if (Params.Length != key.Params.Length)
                {
                    return(false);
                }
                for (int i = 0; i < Params.Length; i++)
                {
                    if (Params[i].Name != key.Params[i].Name ||
                        Params[i].Type != key.Params[i].Type)
                    {
                        return(false);
                    }
                }
                return(true);
            }
예제 #5
0
 public object Eval(string code, IDictionary<string, object> vars)
 {
     var paramsList = new List<ParameterExpression>();
     var valuesList = new List<object>();
     if (ExposeVars) {
         foreach (var varEntry in vars) {
             paramsList.Add(Expression.Parameter(varEntry.Value != null ? varEntry.Value.GetType() : typeof(object), varEntry.Key));
             valuesList.Add(varEntry.Value);
         }
     } else {
         paramsList.Add(Expression.Parameter(typeof(IDictionary<string,object>), VarParamName));
         valuesList.Add( vars );
     }
     Delegate compiledExpr;
     // check in cache
     ExpressionCacheKey cacheKey = new ExpressionCacheKey(code, paramsList.ToArray());
     if (!CacheEnabled || !Cache.TryGetValue(cacheKey, out compiledExpr)) {
         var expression = DynamicExpression.ParseLambda(paramsList.ToArray(), typeof(object), code, null);
         compiledExpr = expression.Compile();
         if (CacheEnabled)
             Cache[cacheKey] = compiledExpr;
     }
     return compiledExpr.DynamicInvoke(valuesList.ToArray());
 }
예제 #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
        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));
        }