private static string ExtractMetadata(EndpointMetadataCollection epMetaCollection)
        {
            var metaFiltered = epMetaCollection.Where(m => !(new[] { typeof(System.Diagnostics.DebuggerStepThroughAttribute) }.Contains(m.GetType())))
                               .Where(t => (!t.GetType().FullName.StartsWith("System.Runtime.CompilerServices.")) && t.GetType().GetProperties().Count() > 0)
                               .GroupBy(m => m.GetType()).Select(group => group.First());

            string metaFallback;
            int    maxDepth = 2;

            using (var strWriter = new StringWriter()) {
                using (var jsonWriter = new Json.DepthCountingJsonTextWriter(strWriter)) {
                    bool include() => jsonWriter.CurrentDepth <= maxDepth;

                    var resolver = new Json.DepthLimitedContractResolver(include);
                    var jsonSerializerSettings = new JsonSerializerSettings()
                    {
                        ContractResolver      = resolver,
                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                        Formatting            = Formatting.Indented,
                        Converters            = new[] { new Json.ShortTypeNameJsonConverter(typeof(System.Type), typeof(System.Reflection.Module), typeof(System.Reflection.MethodInfo)) }
                    };

                    var serializer = JsonSerializer.CreateDefault(jsonSerializerSettings);
                    serializer.Serialize(jsonWriter, metaFiltered);
                }
                metaFallback = strWriter.ToString();
            }

            return(metaFallback);
        }
예제 #2
0
        public MatcherEndpoint(
            Func <RequestDelegate, RequestDelegate> invoker,
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary requiredValues,
            int order,
            EndpointMetadataCollection metadata,
            string displayName)
            : base(metadata, displayName)
        {
            if (invoker == null)
            {
                throw new ArgumentNullException(nameof(invoker));
            }

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

            Invoker = invoker;
            Order   = order;

            Template       = template;
            ParsedTemplate = TemplateParser.Parse(template);

            RequiredValues = requiredValues;
            var mergedDefaults = GetDefaults(ParsedTemplate, defaults);

            Defaults = mergedDefaults;
        }
예제 #3
0
        private MatcherEndpoint CreateEndpoint(
            string template,
            object defaults       = null,
            object requiredValues = null,
            int order             = 0,
            string routeName      = null,
            EndpointMetadataCollection metadataCollection = null)
        {
            if (metadataCollection == null)
            {
                metadataCollection = EndpointMetadataCollection.Empty;
                if (!string.IsNullOrEmpty(routeName))
                {
                    metadataCollection = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) });
                }
            }

            return(new MatcherEndpoint(
                       next => (httpContext) => Task.CompletedTask,
                       RoutePatternFactory.Parse(template, defaults, constraints: null),
                       new RouteValueDictionary(requiredValues),
                       order,
                       metadataCollection,
                       null));
        }
예제 #4
0
    public async Task WriteAsync_Skip_NextWriters_WhenResponseAlreadyStarted()
    {
        // Arrange
        var service = CreateService(
            writers: new List <IProblemDetailsWriter>
        {
            new MetadataBasedWriter("FirstWriter", canWrite: false),
            new MetadataBasedWriter("SecondWriter"),
            new MetadataBasedWriter("FirstWriter"),
        });

        var metadata = new EndpointMetadataCollection(new SampleMetadata()
        {
            ContentType = "application/problem+json"
        });
        var stream  = new MemoryStream();
        var context = new DefaultHttpContext()
        {
            Response = { Body = stream, StatusCode = StatusCodes.Status400BadRequest },
        };

        // Act
        await service.WriteAsync(new() { HttpContext = context, AdditionalMetadata = metadata });

        // Assert
        Assert.Equal("\"SecondWriter\"", Encoding.UTF8.GetString(stream.ToArray()));
    }
        private RouteEndpoint CreateEndpoint(
            string template,
            object defaults = null,
            object metadataRequiredValues = null,
            int order        = 0,
            string routeName = null,
            EndpointMetadataCollection metadataCollection = null)
        {
            if (metadataCollection == null)
            {
                var metadata = new List <object>();
                if (!string.IsNullOrEmpty(routeName))
                {
                    metadata.Add(new RouteNameMetadata(routeName));
                }
                metadataCollection = new EndpointMetadataCollection(metadata);
            }

            return(new RouteEndpoint(
                       TestConstants.EmptyRequestDelegate,
                       RoutePatternFactory.Parse(template, defaults, parameterPolicies: null, requiredValues: metadataRequiredValues),
                       order,
                       metadataCollection,
                       null));
        }
예제 #6
0
    public async Task WriteAsync_Skip_WhenSuccessStatusCode(int statusCode)
    {
        // Arrange
        var service = CreateService(
            writers: new List <IProblemDetailsWriter> {
            new MetadataBasedWriter()
        });
        var stream  = new MemoryStream();
        var context = new DefaultHttpContext()
        {
            Response = { Body = stream, StatusCode = statusCode },
        };
        var metadata = new EndpointMetadataCollection(new SampleMetadata()
        {
            ContentType = "application/problem+json"
        });

        context.SetEndpoint(new Endpoint(context => Task.CompletedTask, metadata, null));

        // Act
        await service.WriteAsync(new() { HttpContext = context });

        // Assert
        Assert.Equal(string.Empty, Encoding.UTF8.GetString(stream.ToArray()));
    }
예제 #7
0
 /// <summary>
 /// Creates a new instance of <see cref="Endpoint"/>.
 /// </summary>
 /// <param name="metadata">
 /// The endpoint <see cref="EndpointMetadataCollection"/>. May be null.
 /// </param>
 /// <param name="displayName">
 /// The informational display name of the endpoint. May be null.
 /// </param>
 protected Endpoint(
     EndpointMetadataCollection metadata,
     string displayName)
 {
     // All are allowed to be null
     Metadata    = metadata ?? EndpointMetadataCollection.Empty;
     DisplayName = displayName;
 }
예제 #8
0
    public void GetRequiredMetadata_ThrowsWhenMetadataNotFound()
    {
        // Arrange
        var metadata = new EndpointMetadataCollection(1, 2);

        // Act
        Assert.Throws <InvalidOperationException>(() => metadata.GetRequiredMetadata <string>());
    }
예제 #9
0
    /// <summary>
    ///     Invokes the authorization filters for the controller action.
    /// </summary>
    /// <returns>Whether the user is authenticated or not.</returns>
    internal static async Task <bool> InvokeAuthorizationFiltersForRequest(this ControllerContext controllerContext, ActionContext actionContext)
    {
        ControllerActionDescriptor actionDescriptor = controllerContext.ActionDescriptor;

        var metadataCollection =
            new EndpointMetadataCollection(actionDescriptor.EndpointMetadata.Union(new[] { actionDescriptor }));

        IReadOnlyList <IAuthorizeData> authorizeData  = metadataCollection.GetOrderedMetadata <IAuthorizeData>();
        IAuthorizationPolicyProvider   policyProvider = controllerContext.HttpContext.RequestServices
                                                        .GetRequiredService <IAuthorizationPolicyProvider>();
        AuthorizationPolicy?policy = await AuthorizationPolicy.CombineAsync(policyProvider, authorizeData);

        if (policy is not null)
        {
            IPolicyEvaluator policyEvaluator =
                controllerContext.HttpContext.RequestServices.GetRequiredService <IPolicyEvaluator>();
            AuthenticateResult authenticateResult =
                await policyEvaluator.AuthenticateAsync(policy, controllerContext.HttpContext);

            if (!authenticateResult.Succeeded)
            {
                return(false);
            }

            // TODO this is super hacky, but we rely on the FeatureAuthorizeHandler can still handle endpoints
            // (The way before .NET 5). The .NET 5 way would need to use han http context, for the "inner" request
            // with the nested controller
            var resource = new Endpoint(null, metadataCollection, null);
            PolicyAuthorizationResult authorizeResult =
                await policyEvaluator.AuthorizeAsync(policy, authenticateResult, controllerContext.HttpContext, resource);

            if (!authorizeResult.Succeeded)
            {
                return(false);
            }
        }

        IList <FilterDescriptor> filters = actionDescriptor.FilterDescriptors;
        var filterGrouping = new FilterGrouping(filters, controllerContext.HttpContext.RequestServices);

        // because the continuation gets built from the inside out we need to reverse the filter list
        // so that least specific filters (Global) get run first and the most specific filters (Action) get run last.
        var authorizationFilters      = filterGrouping.AuthorizationFilters.Reverse().ToList();
        var asyncAuthorizationFilters = filterGrouping.AsyncAuthorizationFilters.Reverse().ToList();

        if (authorizationFilters.Count == 0 && asyncAuthorizationFilters.Count == 0)
        {
            return(true);
        }

        // if the authorization filter returns a result, it means it failed to authorize
        var authorizationFilterContext =
            new AuthorizationFilterContext(actionContext, filters.Select(x => x.Filter).ToArray());

        return(await ExecuteAuthorizationFiltersAsync(authorizationFilterContext, authorizationFilters, asyncAuthorizationFilters));
    }
예제 #10
0
    public void GetRequiredMetadata_CanReturnMetadata()
    {
        // Arrange
        var metadata = new EndpointMetadataCollection(1, "2");

        // Act
        var requiredMetadata = metadata.GetRequiredMetadata <string>();

        Assert.Equal("2", requiredMetadata);
    }
예제 #11
0
    /// <summary>
    /// Creates a new instance of the <see cref="RouteHandlerContext"/>.
    /// </summary>
    /// <param name="methodInfo">The <see cref="MethodInfo"/> associated with the route handler of the current request.</param>
    /// <param name="endpointMetadata">The <see cref="EndpointMetadataCollection"/> associated with the endpoint the filter is targeting.</param>
    /// <param name="applicationServices">The <see cref="IServiceProvider"/> instance used to access the application services.</param>
    public RouteHandlerContext(MethodInfo methodInfo, EndpointMetadataCollection endpointMetadata, IServiceProvider applicationServices)
    {
        ArgumentNullException.ThrowIfNull(methodInfo);
        ArgumentNullException.ThrowIfNull(endpointMetadata);
        ArgumentNullException.ThrowIfNull(applicationServices);

        MethodInfo          = methodInfo;
        EndpointMetadata    = endpointMetadata;
        ApplicationServices = applicationServices;
    }
예제 #12
0
    public void GetOrderedMetadata_CanReturnEmptyCollection()
    {
        // Arrange
        var metadata = new EndpointMetadataCollection(1, 2, 3);

        // Act
        var ordered = metadata.GetOrderedMetadata <string>();

        Assert.Same(Array.Empty <string>(), ordered);
    }
예제 #13
0
 /// <summary>
 /// Creates a new instance of <see cref="Endpoint"/>.
 /// </summary>
 /// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param>
 /// <param name="metadata">
 /// The endpoint <see cref="EndpointMetadataCollection"/>. May be null.
 /// </param>
 /// <param name="displayName">
 /// The informational display name of the endpoint. May be null.
 /// </param>
 public Endpoint(
     RequestDelegate requestDelegate,
     EndpointMetadataCollection metadata,
     string displayName)
 {
     // All are allowed to be null
     RequestDelegate = requestDelegate;
     Metadata        = metadata ?? EndpointMetadataCollection.Empty;
     DisplayName     = displayName;
 }
예제 #14
0
        private MatcherEndpoint CreateEndpoint(ActionDescriptor action, string template, int order, object source)
        {
            RequestDelegate invokerDelegate = (context) =>
            {
                var values    = context.Features.Get <IEndpointFeature>().Values;
                var routeData = new RouteData();
                foreach (var kvp in values)
                {
                    if (kvp.Value != null)
                    {
                        routeData.Values.Add(kvp.Key, kvp.Value);
                    }
                }

                var actionContext = new ActionContext(context, routeData, action);

                var invoker = _invokerFactory.CreateInvoker(actionContext);
                return(invoker.InvokeAsync());
            };

            var metadata = new List <object>();

            // REVIEW: Used for debugging. Consider removing before release
            metadata.Add(source);
            metadata.Add(action);

            // Add filter descriptors to endpoint metadata
            if (action.FilterDescriptors != null && action.FilterDescriptors.Count > 0)
            {
                metadata.AddRange(action.FilterDescriptors.OrderBy(f => f, FilterDescriptorOrderComparer.Comparer).Select(f => f.Filter));
            }

            if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
            {
                foreach (var actionConstraint in action.ActionConstraints)
                {
                    if (actionConstraint is HttpMethodActionConstraint httpMethodActionConstraint)
                    {
                        metadata.Add(new HttpMethodEndpointConstraint(httpMethodActionConstraint.HttpMethods));
                    }
                }
            }

            var metadataCollection = new EndpointMetadataCollection(metadata);
            var endpoint           = new MatcherEndpoint(
                next => invokerDelegate,
                template,
                action.RouteValues,
                order,
                metadataCollection,
                action.DisplayName,
                address: null);

            return(endpoint);
        }
예제 #15
0
        private void InitializeEndpoints()
        {
            // note: this code has haxxx. This will only work in some constrained scenarios
            foreach (var action in _actions.ActionDescriptors.Items)
            {
                if (action.AttributeRouteInfo == null)
                {
                    // Action does not have an attribute route
                    continue;
                }

                RequestDelegate invokerDelegate = (context) =>
                {
                    var values    = context.Features.Get <IEndpointFeature>().Values;
                    var routeData = new RouteData();
                    foreach (var kvp in values)
                    {
                        routeData.Values.Add(kvp.Key, kvp.Value);
                    }

                    var actionContext = new ActionContext(context, routeData, action);

                    var invoker = _invokerFactory.CreateInvoker(actionContext);
                    return(invoker.InvokeAsync());
                };

                var metadata = new List <object>();

                // Add filter descriptors to endpoint metadata
                metadata.AddRange(action.FilterDescriptors.OrderBy(f => f, FilterDescriptorOrderComparer.Comparer).Select(f => f.Filter));

                if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
                {
                    foreach (var actionConstraint in action.ActionConstraints)
                    {
                        if (actionConstraint is HttpMethodActionConstraint httpMethodActionConstraint)
                        {
                            metadata.Add(new HttpMethodEndpointConstraint(httpMethodActionConstraint.HttpMethods));
                        }
                    }
                }

                var metadataCollection = new EndpointMetadataCollection(metadata);

                _endpoints.Add(new MatcherEndpoint(
                                   next => invokerDelegate,
                                   action.AttributeRouteInfo.Template,
                                   action.RouteValues,
                                   action.AttributeRouteInfo.Order,
                                   metadataCollection,
                                   action.DisplayName,
                                   new Address(action.AttributeRouteInfo.Name)));
            }
        }
예제 #16
0
 // TODO wrap these into configuration via json or something better
 public static IEndpointDataSourceBuilder AddProxyEndpoint(this IEndpointDataSourceBuilder builder,
                                                           RoutePattern routePattern,
                                                           RouteValueDictionary requiredValues,
                                                           int order,
                                                           EndpointMetadataCollection metadata,
                                                           string displayName,
                                                           Uri matchUri)
 {
     builder.Endpoints.Add(new ProxyEndpoint(routePattern, requiredValues, order, metadata, displayName, matchUri));
     return(builder);
 }
예제 #17
0
        private static EndpointMetadataCollection BuildEndpointMetadata(
            ActionDescriptor action,
            string routeName,
            object source,
            bool suppressLinkGeneration)
        {
            var metadata = new List <object>();

            // REVIEW: Used for debugging. Consider removing before release
            metadata.Add(source);
            metadata.Add(action);

            if (!string.IsNullOrEmpty(routeName))
            {
                metadata.Add(new RouteNameMetadata(routeName));
            }

            // Add filter descriptors to endpoint metadata
            if (action.FilterDescriptors != null && action.FilterDescriptors.Count > 0)
            {
                metadata.AddRange(action.FilterDescriptors.OrderBy(f => f, FilterDescriptorOrderComparer.Comparer)
                                  .Select(f => f.Filter));
            }

            if (action.ActionConstraints != null && action.ActionConstraints.Count > 0)
            {
                // REVIEW: What is the best way to pick up endpoint constraints of an ActionDescriptor?
                // Currently they need to implement IActionConstraintMetadata
                foreach (var actionConstraint in action.ActionConstraints)
                {
                    if (actionConstraint is HttpMethodActionConstraint httpMethodActionConstraint)
                    {
                        metadata.Add(new HttpMethodEndpointConstraint(httpMethodActionConstraint.HttpMethods));
                    }
                    else if (actionConstraint is IEndpointConstraintMetadata)
                    {
                        // The constraint might have been added earlier, e.g. it is also a filter descriptor
                        if (!metadata.Contains(actionConstraint))
                        {
                            metadata.Add(actionConstraint);
                        }
                    }
                }
            }

            if (suppressLinkGeneration)
            {
                metadata.Add(new SuppressLinkGenerationMetadata());
            }

            var metadataCollection = new EndpointMetadataCollection(metadata);

            return(metadataCollection);
        }
예제 #18
0
    public void GetOrderedMetadata_CanReturnNonEmptyCollection()
    {
        // Arrange
        var metadata = new EndpointMetadataCollection("1", "2");

        // Act
        var ordered1 = metadata.GetOrderedMetadata <string>();
        var ordered2 = metadata.GetOrderedMetadata <string>();

        Assert.Same(ordered1, ordered2);
        Assert.Equal(new string[] { "1", "2" }, ordered1);
    }
예제 #19
0
        private MatcherEndpoint CreateEndpoint(params IEndpointConstraint[] constraints)
        {
            var endpointMetadata = new EndpointMetadataCollection(constraints);

            return(new MatcherEndpoint(
                       (r) => null,
                       RoutePatternFactory.Parse("/"),
                       new RouteValueDictionary(),
                       0,
                       endpointMetadata,
                       ""));
        }
예제 #20
0
        private MatcherEndpoint CreateEndpoint(params IEndpointConstraint[] constraints)
        {
            EndpointMetadataCollection endpointMetadata = new EndpointMetadataCollection(constraints);

            return(new MatcherEndpoint(
                       (r) => null,
                       "",
                       new RouteValueDictionary(),
                       new RouteValueDictionary(),
                       0,
                       endpointMetadata,
                       ""));
        }
예제 #21
0
    public void Constructor_ParamsArray_ContainsValues()
    {
        // Arrange & Act
        var metadata = new EndpointMetadataCollection(1, 2, 3);

        // Assert
        Assert.Equal(3, metadata.Count);

        Assert.Collection(metadata,
                          value => Assert.Equal(1, value),
                          value => Assert.Equal(2, value),
                          value => Assert.Equal(3, value));
    }
예제 #22
0
        public RouteEndpoint BuildEndpoint(IContainer container)
        {
            var handler = CreateHandler(container);
            var pattern = Route.BuildRoutePattern();

            // TODO -- do more to pick up attributes here like [Authorize] and [AllowAnonymous]

            var metadata = new EndpointMetadataCollection(new HttpMethodMetadata(new string[] { Route.HttpMethod }));

            var endpoint = new RouteEndpoint(c => handler.Handle(c), pattern, Route.Order, metadata, Route.Description);

            return(endpoint);
        }
예제 #23
0
        private MatcherEndpoint CreateEndpoint(string routeName, string template, object defaults, int order)
        {
            var metadata = EndpointMetadataCollection.Empty;

            if (!string.IsNullOrEmpty(routeName))
            {
                metadata = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) });
            }

            return(new MatcherEndpoint(
                       next => (httpContext) => Task.CompletedTask,
                       RoutePatternFactory.Parse(template, defaults, constraints: null),
                       new RouteValueDictionary(),
                       order,
                       metadata,
                       "DisplayName"));
        }
예제 #24
0
    private static OpenApiOperation?GetOperationForEndpoint(RouteEndpointBuilder routeEndpointBuilder)
    {
        var pattern         = routeEndpointBuilder.RoutePattern;
        var metadata        = new EndpointMetadataCollection(routeEndpointBuilder.Metadata);
        var methodInfo      = metadata.OfType <MethodInfo>().SingleOrDefault();
        var serviceProvider = routeEndpointBuilder.ServiceProvider;

        if (methodInfo == null || serviceProvider == null)
        {
            return(null);
        }

        var hostEnvironment          = serviceProvider.GetService <IHostEnvironment>();
        var serviceProviderIsService = serviceProvider.GetService <IServiceProviderIsService>();
        var generator = new OpenApiGenerator(hostEnvironment, serviceProviderIsService);

        return(generator.GetOpenApiOperation(methodInfo, metadata, pattern));
    }
예제 #25
0
    public void Constructor_Enumeration_ContainsValues()
    {
        // Arrange & Act
        var metadata = new EndpointMetadataCollection(new List <object>
        {
            1,
            2,
            3,
        });

        // Assert
        Assert.Equal(3, metadata.Count);

        Assert.Collection(metadata,
                          value => Assert.Equal(1, value),
                          value => Assert.Equal(2, value),
                          value => Assert.Equal(3, value));
    }
예제 #26
0
        public ProxyEndpoint(
            RoutePattern routePattern,
            RouteValueDictionary requiredValues,
            int order,
            EndpointMetadataCollection metadata,
            string displayName,
            Uri matchUri)
            : base(metadata, displayName)
        {
            Invoker = (next) => (httpContext) =>
            {
                return(httpContext.ProxyRequest(matchUri));
            };

            RoutePattern   = routePattern ?? throw new ArgumentNullException(nameof(routePattern));
            RequiredValues = requiredValues;
            Order          = order;
            MatchUri       = matchUri;
        }
예제 #27
0
        private RouteEndpoint CreateEndpoint(
            string template,
            object defaults       = null,
            object requiredValues = null,
            int order             = 0,
            string routeName      = null,
            EndpointMetadataCollection metadataCollection = null)
        {
            if (metadataCollection == null)
            {
                metadataCollection = new EndpointMetadataCollection(
                    new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues)));
            }

            return(new RouteEndpoint(
                       (httpContext) => Task.CompletedTask,
                       RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
                       order,
                       metadataCollection,
                       null));
        }
예제 #28
0
        /// <summary>
        /// Initializes a new instance of the <see cref="RouteEndpoint"/> class.
        /// </summary>
        /// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param>
        /// <param name="routePattern">The <see cref="RoutePattern"/> to use in URL matching.</param>
        /// <param name="order">The order assigned to the endpoint.</param>
        /// <param name="metadata">
        /// The <see cref="EndpointMetadataCollection"/> or metadata associated with the endpoint.
        /// </param>
        /// <param name="displayName">The informational display name of the endpoint.</param>
        public RouteEndpoint(
            RequestDelegate requestDelegate,
            RoutePattern routePattern,
            int order,
            EndpointMetadataCollection metadata,
            string displayName)
            : base(requestDelegate, metadata, displayName)
        {
            if (requestDelegate == null)
            {
                throw new ArgumentNullException(nameof(requestDelegate));
            }

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

            RoutePattern = routePattern;
            Order        = order;
        }
예제 #29
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MatcherEndpoint"/> class.
        /// </summary>
        /// <param name="invoker">The delegate to invoke to create a <see cref="RequestDelegate"/>.</param>
        /// <param name="routePattern">The <see cref="RoutePattern"/> to use in URL matching.</param>
        /// <param name="order">The order assigned to the endpoint.</param>
        /// <param name="metadata">
        /// The <see cref="EndpointMetadataCollection"/> or metadata associated with the endpoint.
        /// </param>
        /// <param name="displayName">The informational display name of the endpoint.</param>
        public MatcherEndpoint(
            Func <RequestDelegate, RequestDelegate> invoker,
            RoutePattern routePattern,
            int order,
            EndpointMetadataCollection metadata,
            string displayName)
            : base(metadata, displayName)
        {
            if (invoker == null)
            {
                throw new ArgumentNullException(nameof(invoker));
            }

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

            Invoker      = invoker;
            RoutePattern = routePattern;
            Order        = order;
        }
예제 #30
0
    public void Setup()
    {
        var seeds = new Type[]
        {
            typeof(Metadata1),
            typeof(Metadata2),
            typeof(Metadata3),
            typeof(Metadata4),
            typeof(Metadata5),
            typeof(Metadata6),
            typeof(Metadata7),
            typeof(Metadata8),
            typeof(Metadata9),
        };

        _items = new object[Count];
        for (var i = 0; i < _items.Length; i++)
        {
            _items[i] = seeds[i % seeds.Length];
        }

        _collection = new EndpointMetadataCollection(_items);
    }