Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Add support for updateable ActionDescriptorCollection
Browse files Browse the repository at this point in the history
Fixes #5350
  • Loading branch information
pranavkm committed Oct 20, 2016
1 parent 79e576b commit 40e9efa
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.Extensions.Primitives;

namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
/// <summary>
/// Provides a way to signal invalidation of the cached collection of <see cref="Abstractions.ActionDescriptor" /> from an
/// <see cref="IActionDescriptorCollectionProvider"/>.
/// </summary>
public interface IActionDescriptorChangeProvider
{
/// <summary>
/// Gets a <see cref="IChangeToken"/> used to signal invalidation of cached <see cref="Abstractions.ActionDescriptor"/>
/// instances.
/// </summary>
/// <returns>The <see cref="IChangeToken"/>.</returns>
IChangeToken GetChangeToken();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
/// Provides the currently cached collection of <see cref="Abstractions.ActionDescriptor"/>.
/// </summary>
/// <remarks>
/// The default implementation, does not update the cache, it is up to the user
/// to create or use an implementation that can update the available actions in
/// the application. The implementor is also responsible for updating the
/// <see cref="ActionDescriptorCollection.Version"/> in a thread safe way.
/// The default implementation internallly caches the collection and uses uses
/// <see cref="IActionDescriptorChangeProvider"/> to invalidate this cache, incrementing
/// <see cref="ActionDescriptorCollection.Version"/> the collection is reconstructed.
///
/// Default consumers of this service, are aware of the version and will recache
/// data as appropriate, but rely on the version being unique.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,44 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;

namespace Microsoft.AspNetCore.Mvc.Internal
{
/// <summary>
/// Default implementation of <see cref="IActionDescriptorCollectionProvider"/>.
/// This implementation caches the results at first call, and is not responsible for updates.
/// </summary>
public class ActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider
public class ActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider, IDisposable
{
private readonly IServiceProvider _serviceProvider;
private readonly IActionDescriptorProvider[] _actionDescriptorProviders;
private readonly List<IDisposable> _disposables;
private ActionDescriptorCollection _collection;
private int _version = -1;

/// <summary>
/// Initializes a new instance of the <see cref="ActionDescriptorCollectionProvider" /> class.
/// </summary>
/// <param name="serviceProvider">The application IServiceProvider.</param>
public ActionDescriptorCollectionProvider(IServiceProvider serviceProvider)
/// <param name="actionDescriptorProviders">The sequence of <see cref="IActionDescriptorProvider"/>.</param>
/// <param name="actionDescriptorChangeProviders">The sequence of <see cref="IActionDescriptorChangeProvider"/>.</param>
public ActionDescriptorCollectionProvider(
IEnumerable<IActionDescriptorProvider> actionDescriptorProviders,
IEnumerable<IActionDescriptorChangeProvider> actionDescriptorChangeProviders)
{
_serviceProvider = serviceProvider;
_actionDescriptorProviders = actionDescriptorProviders
.OrderBy(p => p.Order)
.ToArray();

_disposables = new List<IDisposable>();
foreach (var changeTokenProvider in actionDescriptorChangeProviders)
{
_disposables.Add(ChangeToken.OnChange(changeTokenProvider.GetChangeToken, OnChange));
}
}

/// <summary>
Expand All @@ -35,36 +49,48 @@ public ActionDescriptorCollection ActionDescriptors
{
get
{
if (_collection == null)
var result = _collection;

if (result == null)
{
_collection = GetCollection();
result = GetCollection();
_collection = result;
}

return _collection;
return result;
}
}

private ActionDescriptorCollection GetCollection()
{
var providers =
_serviceProvider.GetServices<IActionDescriptorProvider>()
.OrderBy(p => p.Order)
.ToArray();

var context = new ActionDescriptorProviderContext();

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

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

return new ActionDescriptorCollection(
new ReadOnlyCollection<ActionDescriptor>(context.Results), 0);
new ReadOnlyCollection<ActionDescriptor>(context.Results),
Interlocked.Increment(ref _version));
}

private void OnChange()
{
_collection = null;
}

public void Dispose()
{
foreach (var disposable in _disposables)
{
disposable.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -657,17 +657,9 @@ public void ActionNameAttribute_DifferentActionName_UsesActionNameFromActionName
private ControllerActionDescriptor InvokeActionSelector(RouteContext context)
{
var actionDescriptorProvider = GetActionDescriptorProvider();

var serviceContainer = new ServiceCollection();
var list = new List<IActionDescriptorProvider>()
{
actionDescriptorProvider,
};

serviceContainer.AddSingleton(typeof(IEnumerable<IActionDescriptorProvider>), list);

var actionDescriptorCollectionProvider = new ActionDescriptorCollectionProvider(
serviceContainer.BuildServiceProvider());
new[] { actionDescriptorProvider },
Enumerable.Empty<IActionDescriptorChangeProvider>());
var decisionTreeProvider = new ActionSelectorDecisionTreeProvider(actionDescriptorCollectionProvider);

var actionConstraintProviders = new[]
Expand Down Expand Up @@ -827,8 +819,9 @@ private static ActionDescriptor CreateAction(string area, string controller, str

private static ActionConstraintCache GetActionConstraintCache(IActionConstraintProvider[] actionConstraintProviders = null)
{
var services = new ServiceCollection().BuildServiceProvider();
var descriptorProvider = new ActionDescriptorCollectionProvider(services);
var descriptorProvider = new ActionDescriptorCollectionProvider(
Enumerable.Empty<IActionDescriptorProvider>(),
Enumerable.Empty<IActionDescriptorChangeProvider>());
return new ActionConstraintCache(descriptorProvider, actionConstraintProviders.AsEnumerable() ?? new List<IActionConstraintProvider>());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

Expand Down Expand Up @@ -156,8 +159,9 @@ private static IServiceProvider CreateServices()

private static ActionConstraintCache CreateCache(params IActionConstraintProvider[] providers)
{
var services = CreateServices();
var descriptorProvider = new ActionDescriptorCollectionProvider(services);
var descriptorProvider = new ActionDescriptorCollectionProvider(
Enumerable.Empty<IActionDescriptorProvider>(),
Enumerable.Empty<IActionDescriptorChangeProvider>());
return new ActionConstraintCache(descriptorProvider, providers);
}
}
Expand Down
Loading

0 comments on commit 40e9efa

Please sign in to comment.