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