Exemple #1
0
        private void UpdateCollection()
        {
            // Using the lock to initialize writes means that we serialize changes. This eliminates
            // the potential for changes to be processed out of order - the risk is that newer data
            // could be overwritten by older data.
            lock (_lock)
            {
                var context = new ActionDescriptorProviderContext();

                for (var i = 0; i < _actionDescriptorProviders.Length; i++)
                {
                    _actionDescriptorProviders[i].OnProvidersExecuting(context);
                }

                for (var i = _actionDescriptorProviders.Length - 1; i >= 0; i--)
                {
                    _actionDescriptorProviders[i].OnProvidersExecuted(context);
                }

                // The sequence for an update is important because we don't want anyone to obtain
                // the new change token but the old action descriptor collection.
                // 1. Obtain the old cancellation token source (don't trigger it yet)
                // 2. Set the new action descriptor collection
                // 3. Set the new change token
                // 4. Trigger the old cancellation token source
                //
                // Consumers who poll will observe a new action descriptor collection at step 2 - they will see
                // the new collection and ignore the change token.
                //
                // Consumers who listen to the change token will requery at step 4 - they will see the new collection
                // and new change token.
                //
                // Anyone who acquires the collection and change token between steps 2 and 3 will be notified of
                // a no-op change at step 4.

                // Step 1.
                var oldCancellationTokenSource = _cancellationTokenSource;

                // Step 2.
                _collection = new ActionDescriptorCollection(
                    new ReadOnlyCollection <ActionDescriptor>(context.Results),
                    _version++);

                // Step 3.
                _cancellationTokenSource = new CancellationTokenSource();
                _changeToken             = new CancellationChangeToken(_cancellationTokenSource.Token);

                // Step 4 - might be null if it's the first time.
                oldCancellationTokenSource?.Cancel();
            }
        }
        public static ActionSelectionTable <ActionDescriptor> Create(ActionDescriptorCollection actions)
        {
            return(CreateCore <ActionDescriptor>(

                       // We need to store the version so the cache can be invalidated if the actions change.
                       version: actions.Version,

                       // For action selection, ignore attribute routed actions
                       items: actions.Items.Where(a => a.AttributeRouteInfo == null),

                       getRouteKeys: a => a.RouteValues.Keys,
                       getRouteValue: (a, key) =>
            {
                a.RouteValues.TryGetValue(key, out var value);
                return value ?? string.Empty;
            }));
        }
        private ApiDescriptionGroupCollection GetCollection(ActionDescriptorCollection actionDescriptors)
        {
            var context = new ApiDescriptionProviderContext(actionDescriptors.Items);

            foreach (var provider in _apiDescriptionProviders)
            {
                provider.OnProvidersExecuting(context);
            }

            for (var i = _apiDescriptionProviders.Length - 1; i >= 0; i--)
            {
                _apiDescriptionProviders[i].OnProvidersExecuted(context);
            }

            var groups = context.Results
                .GroupBy(d => d.GroupName)
                .Select(g => new ApiDescriptionGroup(g.Key, g.ToArray()))
                .ToArray();

            return new ApiDescriptionGroupCollection(groups, actionDescriptors.Version);
        }
 public CustomActionDescriptorCollectionProvider(ControllerActionDescriptor[] actionDescriptors)
 {
     ActionDescriptors = new ActionDescriptorCollection(actionDescriptors, version: 1);
 }
        public void ActionDescriptors_UpdatesAndResubscripes_WhenChangeTokenTriggers()
        {
            // Arrange
            var actionDescriptorProvider = new Mock <IActionDescriptorProvider>();
            var expected1 = new ActionDescriptor();
            var expected2 = new ActionDescriptor();
            var expected3 = new ActionDescriptor();

            var invocations = 0;

            actionDescriptorProvider
            .Setup(p => p.OnProvidersExecuting(It.IsAny <ActionDescriptorProviderContext>()))
            .Callback((ActionDescriptorProviderContext context) =>
            {
                if (invocations == 0)
                {
                    context.Results.Add(expected1);
                }
                else if (invocations == 1)
                {
                    context.Results.Add(expected2);
                }
                else
                {
                    context.Results.Add(expected3);
                }

                invocations++;
            });
            var changeProvider = new TestChangeProvider();
            var actionDescriptorCollectionProvider = new DefaultActionDescriptorCollectionProvider(
                new[] { actionDescriptorProvider.Object },
                new[] { changeProvider });

            // Act - 1
            var changeToken1 = actionDescriptorCollectionProvider.GetChangeToken();
            var collection1  = actionDescriptorCollectionProvider.ActionDescriptors;

            ActionDescriptorCollection captured = null;

            changeToken1.RegisterChangeCallback((_) =>
            {
                captured = actionDescriptorCollectionProvider.ActionDescriptors;
            }, null);

            // Assert - 1
            Assert.False(changeToken1.HasChanged);
            Assert.Equal(0, collection1.Version);
            Assert.Collection(collection1.Items,
                              item => Assert.Same(expected1, item));

            // Act - 2
            changeProvider.TokenSource.Cancel();
            var changeToken2 = actionDescriptorCollectionProvider.GetChangeToken();
            var collection2  = actionDescriptorCollectionProvider.ActionDescriptors;

            changeToken2.RegisterChangeCallback((_) =>
            {
                captured = actionDescriptorCollectionProvider.ActionDescriptors;
            }, null);

            // Assert - 2
            Assert.NotSame(changeToken1, changeToken2);
            Assert.True(changeToken1.HasChanged);
            Assert.False(changeToken2.HasChanged);

            Assert.NotSame(collection1, collection2);
            Assert.NotNull(captured);
            Assert.Same(captured, collection2);
            Assert.Equal(1, collection2.Version);
            Assert.Collection(collection2.Items,
                              item => Assert.Same(expected2, item));

            // Act - 3
            changeProvider.TokenSource.Cancel();
            var changeToken3 = actionDescriptorCollectionProvider.GetChangeToken();
            var collection3  = actionDescriptorCollectionProvider.ActionDescriptors;

            // Assert - 3
            Assert.NotSame(changeToken2, changeToken3);
            Assert.True(changeToken2.HasChanged);
            Assert.False(changeToken3.HasChanged);

            Assert.NotSame(collection2, collection3);
            Assert.NotNull(captured);
            Assert.Same(captured, collection3);
            Assert.Equal(2, collection3.Version);
            Assert.Collection(collection3.Items,
                              item => Assert.Same(expected3, item));
        }
Exemple #6
0
 public static IEnumerable <PageActionDescriptor> PageActionDescriptors(this ActionDescriptorCollection actionDescriptorCollection) => actionDescriptorCollection.Items.OfType <PageActionDescriptor>();
Exemple #7
0
 public static IEnumerable <ControllerActionDescriptor> ControllerActionDescriptors(this ActionDescriptorCollection actionDescriptorCollection) => actionDescriptorCollection.Items.OfType <ControllerActionDescriptor>();
            public Cache(ActionDescriptorCollection actions)
            {
                // We need to store the version so the cache can be invalidated if the actions change.
                Version = actions.Version;

                // We need to build two maps for all of the route values.
                OrdinalEntries           = new Dictionary <string[], List <ActionDescriptor> >(StringArrayComparer.Ordinal);
                OrdinalIgnoreCaseEntries = new Dictionary <string[], List <ActionDescriptor> >(StringArrayComparer.OrdinalIgnoreCase);

                // We need to first identify of the keys that action selection will look at (in route data).
                // We want to only consider conventionally routed actions here.
                var routeKeys = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                for (var i = 0; i < actions.Items.Count; i++)
                {
                    var action = actions.Items[i];
                    if (action.AttributeRouteInfo == null)
                    {
                        // This is a conventionally routed action - so make sure we include its keys in the set of
                        // known route value keys.
                        foreach (var kvp in action.RouteValues)
                        {
                            routeKeys.Add(kvp.Key);
                        }
                    }
                }

                // We need to hold on to an ordered set of keys for the route values. We'll use these later to
                // extract the set of route values from an incoming request to compare against our maps of known
                // route values.
                RouteKeys = routeKeys.ToArray();

                for (var i = 0; i < actions.Items.Count; i++)
                {
                    var action = actions.Items[i];
                    if (action.AttributeRouteInfo != null)
                    {
                        // This only handles conventional routing. Ignore attribute routed actions.
                        continue;
                    }

                    // This is a conventionally routed action - so we need to extract the route values associated
                    // with this action (in order) so we can store them in our dictionaries.
                    var routeValues = new string[RouteKeys.Length];
                    for (var j = 0; j < RouteKeys.Length; j++)
                    {
                        action.RouteValues.TryGetValue(RouteKeys[j], out routeValues[j]);
                    }

                    if (!OrdinalIgnoreCaseEntries.TryGetValue(routeValues, out var entries))
                    {
                        entries = new List <ActionDescriptor>();
                        OrdinalIgnoreCaseEntries.Add(routeValues, entries);
                    }

                    entries.Add(action);

                    // We also want to add the same (as in reference equality) list of actions to the ordinal entries.
                    // We'll keep updating `entries` to include all of the actions in the same equivalence class -
                    // meaning, all conventionally routed actions for which the route values are equal ignoring case.
                    //
                    // `entries` will appear in `OrdinalIgnoreCaseEntries` exactly once and in `OrdinalEntries` once
                    // for each variation of casing that we've seen.
                    if (!OrdinalEntries.ContainsKey(routeValues))
                    {
                        OrdinalEntries.Add(routeValues, entries);
                    }
                }
            }