Ejemplo n.º 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="HtmlHelper{TModel}"/> class.
        /// </summary>
        public HtmlHelper(
            IHtmlGenerator htmlGenerator,
            ICompositeViewEngine viewEngine,
            IModelMetadataProvider metadataProvider,
#pragma warning disable PUB0001 // Pubternal type in public API
            IViewBufferScope bufferScope,
#pragma warning restore PUB0001
            HtmlEncoder htmlEncoder,
            UrlEncoder urlEncoder,
#pragma warning disable PUB0001 // Pubternal type in public API
            ExpressionTextCache expressionTextCache
#pragma warning restore PUB0001
            )
            : base(
                htmlGenerator,
                viewEngine,
                metadataProvider,
                bufferScope,
                htmlEncoder,
                urlEncoder)
        {
            if (expressionTextCache == null)
            {
                throw new ArgumentNullException(nameof(expressionTextCache));
            }

            _expressionTextCache = expressionTextCache;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Creates a  new <see cref="ModelExpressionProvider"/>.
        /// </summary>
        /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
        /// <param name="expressionTextCache">The <see cref="ExpressionTextCache"/>.</param>
        public ModelExpressionProvider(
            IModelMetadataProvider modelMetadataProvider,
            ExpressionTextCache expressionTextCache)
        {
            if (modelMetadataProvider == null)
            {
                throw new ArgumentNullException(nameof(modelMetadataProvider));
            }

            if (expressionTextCache == null)
            {
                throw new ArgumentNullException(nameof(expressionTextCache));
            }

            _modelMetadataProvider = modelMetadataProvider;
            _expressionTextCache   = expressionTextCache;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Creates a  new <see cref="ModelExpressionProvider"/>.
        /// </summary>
        /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
        /// <param name="expressionTextCache">The <see cref="ExpressionTextCache"/>.</param>
        public ModelExpressionProvider(
            IModelMetadataProvider modelMetadataProvider,
#pragma warning disable PUB0001 // Pubternal type in public API
            ExpressionTextCache expressionTextCache
#pragma warning restore PUB0001
            )
        {
            if (modelMetadataProvider == null)
            {
                throw new ArgumentNullException(nameof(modelMetadataProvider));
            }

            if (expressionTextCache == null)
            {
                throw new ArgumentNullException(nameof(expressionTextCache));
            }

            _modelMetadataProvider = modelMetadataProvider;
            _expressionTextCache   = expressionTextCache;
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="HtmlHelper{TModel}"/> class.
        /// </summary>
        public HtmlHelper(
            IHtmlGenerator htmlGenerator,
            ICompositeViewEngine viewEngine,
            IModelMetadataProvider metadataProvider,
            IViewBufferScope bufferScope,
            HtmlEncoder htmlEncoder,
            UrlEncoder urlEncoder,
            ExpressionTextCache expressionTextCache)
            : base(
                htmlGenerator,
                viewEngine,
                metadataProvider,
                bufferScope,
                htmlEncoder,
                urlEncoder)
        {
            if (expressionTextCache == null)
            {
                throw new ArgumentNullException(nameof(expressionTextCache));
            }

            _expressionTextCache = expressionTextCache;
        }
Ejemplo n.º 5
0
        public static string GetExpressionText(LambdaExpression expression, ExpressionTextCache expressionTextCache)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            if (expressionTextCache != null &&
                expressionTextCache.Entries.TryGetValue(expression, out var expressionText))
            {
                return(expressionText);
            }

            // Determine size of string needed (length) and number of segments it contains (segmentCount). Put another
            // way, segmentCount tracks the number of times the loop below should iterate. This avoids adding ".model"
            // and / or an extra leading "." and then removing them after the loop. Other information collected in this
            // first loop helps with length and segmentCount adjustments. doNotCache is somewhat separate: If
            // true, expression strings are not cached for the expression.
            //
            // After the corrections below the first loop, length is usually exactly the size of the returned string.
            // However when containsIndexers is true, the calculation is approximate because either evaluating indexer
            // expressions multiple times or saving indexer strings can get expensive. Optimizing for the common case
            // of a collection (not a dictionary) with less than 100 elements. If that assumption proves to be
            // incorrect, the StringBuilder will be enlarged but hopefully just once.
            var doNotCache   = false;
            var lastIsModel  = false;
            var length       = 0;
            var segmentCount = 0;
            var trailingMemberExpressions = 0;

            var part = expression.Body;

            while (part != null)
            {
                switch (part.NodeType)
                {
                case ExpressionType.Call:
                    // Will exit loop if at Method().Property or [i,j].Property. In that case (like [i].Property),
                    // don't cache and don't remove ".Model" (if that's .Property).
                    doNotCache  = true;
                    lastIsModel = false;

                    var methodExpression = (MethodCallExpression)part;
                    if (IsSingleArgumentIndexer(methodExpression))
                    {
                        length += "[99]".Length;
                        part    = methodExpression.Object;
                        segmentCount++;
                        trailingMemberExpressions = 0;
                    }
                    else
                    {
                        // Unsupported.
                        part = null;
                    }
                    break;

                case ExpressionType.ArrayIndex:
                    var binaryExpression = (BinaryExpression)part;

                    doNotCache  = true;
                    lastIsModel = false;
                    length     += "[99]".Length;
                    part        = binaryExpression.Left;
                    segmentCount++;
                    trailingMemberExpressions = 0;
                    break;

                case ExpressionType.MemberAccess:
                    var memberExpressionPart = (MemberExpression)part;
                    var name = memberExpressionPart.Member.Name;

                    // If identifier contains "__", it is "reserved for use by the implementation" and likely
                    // compiler- or Razor-generated e.g. the name of a field in a delegate's generated class.
                    if (name.Contains("__"))
                    {
                        // Exit loop.
                        part = null;
                    }
                    else
                    {
                        lastIsModel = string.Equals("model", name, StringComparison.OrdinalIgnoreCase);
                        length     += name.Length + 1;
                        part        = memberExpressionPart.Expression;
                        segmentCount++;
                        trailingMemberExpressions++;
                    }
                    break;

                case ExpressionType.Parameter:
                    // Unsupported but indicates previous member access was not the view's Model.
                    lastIsModel = false;
                    part        = null;
                    break;

                default:
                    // Unsupported.
                    part = null;
                    break;
                }
            }

            // If name would start with ".model", then strip that part away.
            if (lastIsModel)
            {
                length -= ".model".Length;
                segmentCount--;
                trailingMemberExpressions--;
            }

            // Trim the leading "." if present. The loop below special-cases the last property to avoid this addition.
            if (trailingMemberExpressions > 0)
            {
                length--;
            }

            Debug.Assert(segmentCount >= 0);
            if (segmentCount == 0)
            {
                Debug.Assert(!doNotCache);
                if (expressionTextCache != null)
                {
                    expressionTextCache.Entries.TryAdd(expression, string.Empty);
                }

                return(string.Empty);
            }

            var builder = new StringBuilder(length);

            part = expression.Body;
            while (part != null && segmentCount > 0)
            {
                segmentCount--;
                switch (part.NodeType)
                {
                case ExpressionType.Call:
                    Debug.Assert(doNotCache);
                    var methodExpression = (MethodCallExpression)part;

                    InsertIndexerInvocationText(builder, methodExpression.Arguments.Single(), expression);

                    part = methodExpression.Object;
                    break;

                case ExpressionType.ArrayIndex:
                    Debug.Assert(doNotCache);
                    var binaryExpression = (BinaryExpression)part;

                    InsertIndexerInvocationText(builder, binaryExpression.Right, expression);

                    part = binaryExpression.Left;
                    break;

                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression)part;
                    var name             = memberExpression.Member.Name;
                    Debug.Assert(!name.Contains("__"));

                    builder.Insert(0, name);
                    if (segmentCount > 0)
                    {
                        // One or more parts to the left of this part are coming.
                        builder.Insert(0, '.');
                    }

                    part = memberExpression.Expression;
                    break;

                default:
                    // Should be unreachable due to handling in above loop.
                    Debug.Assert(false);
                    break;
                }
            }

            Debug.Assert(segmentCount == 0);
            expressionText = builder.ToString();
            if (expressionTextCache != null && !doNotCache)
            {
                expressionTextCache.Entries.TryAdd(expression, expressionText);
            }

            return(expressionText);
        }