diff --git a/benchmarks/Microsoft.AspNetCore.Mvc.Performance/MvcEndpointDatasourceBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Mvc.Performance/MvcEndpointDatasourceBenchmark.cs new file mode 100644 index 0000000000..982b8d4e22 --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Mvc.Performance/MvcEndpointDatasourceBenchmark.cs @@ -0,0 +1,131 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ActionConstraints; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Mvc.Internal; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.AspNetCore.Mvc.Performance +{ + public class MvcEndpointDataSourceBenchmark + { + private const string DefaultRoute = "{Controller=Home}/{Action=Index}/{id?}"; + + private MockActionDescriptorCollectionProvider _conventionalActionProvider; + private MockActionDescriptorCollectionProvider _attributeActionProvider; + private List _conventionalEndpointInfos; + + [Params(1, 100, 1000)] + public int ActionCount; + + [GlobalSetup] + public void Setup() + { + _conventionalActionProvider = new MockActionDescriptorCollectionProvider( + Enumerable.Range(0, ActionCount).Select(i => CreateActionDescriptor(i, false)).ToList() + ); + + _attributeActionProvider = new MockActionDescriptorCollectionProvider( + Enumerable.Range(0, ActionCount).Select(i => CreateActionDescriptor(i, true)).ToList() + ); + + _conventionalEndpointInfos = new List + { + new MvcEndpointInfo( + "Default", + DefaultRoute, + new RouteValueDictionary(), + new Dictionary(), + new RouteValueDictionary(), + new MockInlineConstraintResolver()) + }; + } + + [Benchmark] + public void AttributeRouteEndpoints() + { + var endpointDataSource = CreateMvcEndpointDataSource(_attributeActionProvider); + var endpoints = endpointDataSource.Endpoints; + } + + [Benchmark] + public void ConventionalEndpoints() + { + var endpointDataSource = CreateMvcEndpointDataSource(_conventionalActionProvider); + endpointDataSource.ConventionalEndpointInfos.AddRange(_conventionalEndpointInfos); + var endpoints = endpointDataSource.Endpoints; + } + + private ActionDescriptor CreateActionDescriptor(int id, bool attributeRoute) + { + var actionDescriptor = new ActionDescriptor + { + RouteValues = new Dictionary + { + ["Controller"] = "Controller" + id, + ["Action"] = "Index" + }, + DisplayName = "Action " + id + }; + + if (attributeRoute) + { + actionDescriptor.AttributeRouteInfo = new AttributeRouteInfo + { + Template = DefaultRoute + }; + } + + return actionDescriptor; + } + + private MvcEndpointDataSource CreateMvcEndpointDataSource( + IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) + { + var dataSource = new MvcEndpointDataSource( + actionDescriptorCollectionProvider, + new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty())), + Array.Empty(), + new MockServiceProvider()); + + return dataSource; + } + + private class MockActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider + { + public MockActionDescriptorCollectionProvider(List actionDescriptors) + { + ActionDescriptors = new ActionDescriptorCollection(actionDescriptors, 0); + } + + public ActionDescriptorCollection ActionDescriptors { get; } + } + + private class MockServiceProvider : IServiceProvider + { + public object GetService(Type serviceType) + { + throw new NotImplementedException(); + } + } + + private class MockInlineConstraintResolver : IInlineConstraintResolver + { + public IRouteConstraint ResolveConstraint(string inlineConstraint) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs index 31781090a1..14bede0243 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs @@ -36,4 +36,6 @@ using Microsoft.AspNetCore.Mvc.Formatters; [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ViewFeatures.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Views.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] + [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]