Example #1
0
 /// <summary>
 /// Maps the specified versioned OData routes. When the <paramref name="batchHandler"/> is provided, it will create a
 /// '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="configuration">The extended <see cref="HttpConfiguration">HTTP configuration</see>.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="models">The <see cref="IEnumerable{T}">sequence</see> of <see cref="IEdmModel">EDM models</see> to use for parsing OData paths.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler">OData batch handler</see>.</param>
 /// <returns>The <see cref="IReadOnlyList{T}">read-only list</see> of added <see cref="ODataRoute">OData routes</see>.</returns>
 /// <remarks>The specified <paramref name="models"/> must contain the <see cref="ApiVersionAnnotation">API version annotation</see>.  This annotation is
 /// automatically applied when you use the <see cref="VersionedODataModelBuilder"/> and call <see cref="VersionedODataModelBuilder.GetEdmModels"/> to
 /// create the <paramref name="models"/>.</remarks>
 public static IReadOnlyList <ODataRoute> MapVersionedODataRoutes(
     this HttpConfiguration configuration,
     string routeName,
     string routePrefix,
     IEnumerable <IEdmModel> models,
     ODataBatchHandler batchHandler) =>
 MapVersionedODataRoutes(configuration, routeName, routePrefix, models, new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault(), batchHandler);
Example #2
0
        private static void MapHttpBatchRoute(HttpRouteCollection routes, ODataBatchHandler batchHandler, string routeName, string routePrefix)
        {
            batchHandler.ODataRouteName = routeName;
            var batchTemplate = string.IsNullOrEmpty(routePrefix) ? ODataRouteConstants.Batch : routePrefix + '/' + ODataRouteConstants.Batch;

            routes.MapHttpBatchRoute(routeName + "Batch", batchTemplate, batchHandler);
        }
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TApi"></typeparam>
        /// <param name="config"></param>
        /// <param name="routeName"></param>
        /// <param name="routePrefix"></param>
        /// <param name="allowBatching"></param>
        /// <param name="httpServer"></param>
        /// <returns></returns>
        public static HttpConfiguration MapRestier <TApi>(this HttpConfiguration config, string routeName, string routePrefix, bool allowBatching, HttpServer httpServer)
        {
            ODataBatchHandler batchHandler = null;
            var conventions = CreateRestierRoutingConventions(config, routeName);

            if (allowBatching)
            {
                if (httpServer == null)
                {
                    throw new ArgumentNullException(nameof(httpServer), owinException);
                }

#pragma warning disable IDE0067 // Dispose objects before losing scope
                batchHandler = new RestierBatchHandler(httpServer)
                {
                    ODataRouteName = routeName
                };
#pragma warning restore IDE0067 // Dispose objects before losing scope
            }

            config.MapODataServiceRoute(routeName, routePrefix, (builder) =>
            {
                builder.AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp => conventions);
                if (batchHandler != null)
                {
                    //RWM: DO NOT simplify this generic signature. It HAS to stay this way, otherwise the code breaks.
                    builder.AddService <ODataBatchHandler>(ServiceLifetime.Singleton, sp => batchHandler);
                }
            });

            return(config);
        }
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TApi"></typeparam>
        /// <param name="config"></param>
        /// <param name="routeName"></param>
        /// <param name="routePrefix"></param>
        /// <param name="allowBatching"></param>
        /// <returns></returns>
        public static HttpConfiguration MapRestier <TApi>(this HttpConfiguration config, string routeName, string routePrefix, bool allowBatching = true)
        {
            ODataBatchHandler batchHandler = null;
            var conventions = CreateRestierRoutingConventions(config, routeName);

            if (allowBatching)
            {
#pragma warning disable IDE0067 // Dispose objects before losing scope
                batchHandler = new RestierBatchHandler(GlobalConfiguration.DefaultServer)
                {
                    ODataRouteName = routeName
                };
#pragma warning restore IDE0067 // Dispose objects before losing scope
            }

            config.MapODataServiceRoute(routeName, routePrefix, (builder) =>
            {
                builder.AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp => conventions);
                if (batchHandler != null)
                {
                    builder.AddService(ServiceLifetime.Singleton, sp => batchHandler);
                }
            });

            return(config);
        }
Example #5
0
 public static void MapODataRoute(this HttpRouteCollection routes, string routeName, string routePrefix,
                                  IEdmModel model, IODataPathHandler pathHandler, IEnumerable <IODataRoutingConvention> routingConventions,
                                  ODataBatchHandler batchHandler)
 {
     Extensions.HttpRouteCollectionExtensions.MapODataServiceRoute(routes, routeName, routePrefix, model,
                                                                   pathHandler, routingConventions, batchHandler);
 }
Example #6
0
 /// <summary>
 /// Maps a versioned OData route.
 /// </summary>
 /// <param name="configuration">The extended <see cref="HttpConfiguration">HTTP configuration</see>.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="model">The <see cref="IEdmModel">EDM model</see> to use for parsing OData paths.</param>
 /// <param name="apiVersion">The <see cref="ApiVersion">API version</see> associated with the model.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler">OData batch handler</see>.</param>
 /// <returns>The mapped <see cref="ODataRoute">OData route</see>.</returns>
 /// <remarks>The <see cref="ApiVersionAnnotation">API version annotation</see> will be added or updated on the specified <paramref name="model"/> using
 /// the provided <paramref name="apiVersion">API version</paramref>.</remarks>
 public static ODataRoute MapVersionedODataRoute(
     this HttpConfiguration configuration,
     string routeName,
     string routePrefix,
     IEdmModel model,
     ApiVersion apiVersion,
     ODataBatchHandler batchHandler) =>
 MapVersionedODataRoute(configuration, routeName, routePrefix, model, apiVersion, new DefaultODataPathHandler(), ODataRoutingConventions.CreateDefault(), batchHandler, null);
 /// <summary>
 /// Maps the specified versioned OData routes.
 /// </summary>
 /// <param name="configuration">The extended <see cref="HttpConfiguration">HTTP configuration</see>.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="models">The <see cref="IEnumerable{T}">sequence</see> of <see cref="IEdmModel">EDM models</see> to use for parsing OData paths.</param>
 /// <param name="configureAction">The configuring action to add the services to the root container.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler">OData batch handler</see>.</param>
 /// <returns>The <see cref="IReadOnlyList{T}">read-only list</see> of added <see cref="ODataRoute">OData routes</see>.</returns>
 /// <remarks>The specified <paramref name="models"/> must contain the <see cref="ApiVersionAnnotation">API version annotation</see>.  This annotation is
 /// automatically applied when you use the <see cref="VersionedODataModelBuilder"/> and call <see cref="VersionedODataModelBuilder.GetEdmModels"/> to
 /// create the <paramref name="models"/>.</remarks>
 public static IReadOnlyList <ODataRoute> MapVersionedODataRoutes(
     this HttpConfiguration configuration,
     string routeName,
     string routePrefix,
     IEnumerable <IEdmModel> models,
     Action <IContainerBuilder> configureAction,
     ODataBatchHandler batchHandler) =>
 MapVersionedODataRoutes(configuration, routeName, routePrefix, models, configureAction, default, batchHandler);
Example #8
0
 /// <summary>
 /// Maps a versioned OData route.
 /// </summary>
 /// <param name="builder">The extended <see cref="IRouteBuilder">route builder</see>.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="model">The <see cref="IEdmModel">EDM model</see> to use for parsing OData paths.</param>
 /// <param name="apiVersion">The <see cref="ApiVersion">API version</see> associated with the model.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler">OData batch handler</see>.</param>
 /// <returns>The mapped <see cref="ODataRoute">OData route</see>.</returns>
 /// <remarks>The <see cref="ApiVersionAnnotation">API version annotation</see> will be added or updated on the specified <paramref name="model"/> using
 /// the provided <paramref name="apiVersion">API version</paramref>.</remarks>
 public static ODataRoute MapVersionedODataRoute(
     this IRouteBuilder builder,
     string routeName,
     string routePrefix,
     IEdmModel model,
     ApiVersion apiVersion,
     ODataBatchHandler batchHandler) =>
 MapVersionedODataRoute(builder, routeName, routePrefix, model, apiVersion, new DefaultODataPathHandler(), VersionedODataRoutingConventions.CreateDefault(), batchHandler);
        private static void MapService <TContext>(
            HttpConfiguration config,
            string routeName   = "DynamicODataRoute",
            string routePrefix = "odata",
            IList <IODataRoutingConvention> conventions = null,
            ODataBatchHandler batchHandler = null)
            where TContext : DbContext, new()
        {
            config.EnableDependencyInjection();

            ODataModelBuilder builder = new ODataConventionModelBuilder();
            //Register all properties of IComponentModel as Odata entities
            MethodInfo baseMethod = typeof(ODataModelBuilder).GetMethod(nameof(builder.EntitySet));
            //All of this reflection only affects the startup time and not during the actual running of the program
            var entityProperties = typeof(TContext).GetProperties(BindingFlags.Instance |
                                                                  BindingFlags.Public | BindingFlags.DeclaredOnly)
                                   .Where(p => p.PropertyType.GetInterfaces().Contains(typeof(IQueryable)));

            foreach (var prop in entityProperties)
            {
                var        entityType = prop.PropertyType.GetGenericArguments()[0];
                MethodInfo generic    = baseMethod.MakeGenericMethod(entityType);

                generic.Invoke(builder, new[] { prop.Name });
            }
            config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
            config.AddODataQueryFilter();

            if (conventions == null)
            {
                conventions = ODataRoutingConventions.CreateDefault();
            }

            conventions.Insert(0, new KeyEntityRoutingConvention());
            conventions.Insert(0, new DynamicNavigationPropertyConvention());
            conventions.Insert(0, new PropertyAccessConvention());


            var entityModels = builder.GetEdmModel();

            config.MapODataServiceRoute(
                routeName: routeName,
                routePrefix: routePrefix,
                model: entityModels,
                pathHandler: new DefaultODataPathHandler(),
                routingConventions: conventions,
                batchHandler: batchHandler);

            var controllerSelector = new GenericControllerSelector <TContext>(config, builder.EntitySets, config.Services.GetHttpControllerSelector() ?? new DefaultHttpControllerSelector(config));

            config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
            foreach (var item in controllerSelector.GetEntityControllers())
            {
                var entitySetInfo = entityModels.EntityContainer.FindEntitySet(item.Key);
                item.Value.Configuration.Services.Replace(typeof(IHttpActionSelector), new GenericActionSelector(config.Services.GetActionSelector(), item, entitySetInfo));
            }
        }
Example #10
0
 public static ODataRoute MapVersionedODataRoute(
     this HttpConfiguration configuration,
     string routeName,
     string routePrefix,
     IEdmModel model,
     ApiVersion apiVersion,
     IODataPathHandler pathHandler,
     IEnumerable <IODataRoutingConvention> routingConventions,
     ODataBatchHandler batchHandler) =>
 MapVersionedODataRoute(configuration, routeName, routePrefix, model, apiVersion, pathHandler, routingConventions, batchHandler, null);
Example #11
0
        public static IReadOnlyList<ODataRoute> MapVersionedODataRoutes(
            this HttpConfiguration configuration,
            string routeName,
            string routePrefix,
            IEnumerable<IEdmModel> models,
            IODataPathHandler pathHandler,
            IEnumerable<IODataRoutingConvention> routingConventions,
            ODataBatchHandler batchHandler )
        {
            Arg.NotNull( configuration, nameof( configuration ) );
            Arg.NotNull( models, nameof( models ) );
            Contract.Ensures( Contract.Result<IReadOnlyList<ODataRoute>>() != null );

            var routeConventions = EnsureConventions( routingConventions.ToList() );
            var routes = configuration.Routes;

            if ( !IsNullOrEmpty( routePrefix ) )
            {
                routePrefix = routePrefix.TrimEnd( '/' );
            }

            if ( batchHandler != null )
            {
                var batchTemplate = IsNullOrEmpty( routePrefix ) ? ODataRouteConstants.Batch : routePrefix + '/' + ODataRouteConstants.Batch;
                routes.MapHttpBatchRoute( routeName + "Batch", batchTemplate, batchHandler );
            }

            configuration.SetResolverSettings( pathHandler );
            routeConventions.Insert( 0, null );

            var odataRoutes = new List<ODataRoute>();
            var unversionedConstraints = new List<IHttpRouteConstraint>();

            foreach ( var model in models )
            {
                var versionedRouteName = routeName;
                var routeConstraint = default( ODataPathRouteConstraint );

                routeConventions[0] = new VersionedAttributeRoutingConvention( model, configuration );
                routeConstraint = new ODataPathRouteConstraint( pathHandler, model, versionedRouteName, routeConventions.ToArray() );
                unversionedConstraints.Add( routeConstraint );
                routeConstraint = MakeVersionedODataRouteConstraint( routeConstraint, pathHandler, routeConventions, model, ref versionedRouteName );

                var route = new ODataRoute( routePrefix, routeConstraint );

                AddApiVersionConstraintIfNecessary( route );
                routes.Add( versionedRouteName, route );
                odataRoutes.Add( route );
            }

            AddRouteToRespondWithBadRequestWhenAtLeastOneRouteCouldMatch( routeName, routePrefix, routes, odataRoutes, unversionedConstraints );

            return odataRoutes;
        }
Example #12
0
        /// <summary>
        /// Maps a versioned OData route. When the <paramref name="batchHandler"/> is provided, it will create a '$batch' endpoint to handle the batch requests.
        /// </summary>
        /// <param name="builder">The extended <see cref="IRouteBuilder">route builder</see>.</param>
        /// <param name="routeName">The name of the route to map.</param>
        /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
        /// <param name="model">The <see cref="IEdmModel">EDM model</see> to use for parsing OData paths.</param>
        /// <param name="apiVersion">The <see cref="ApiVersion">API version</see> associated with the model.</param>
        /// <param name="pathHandler">The <see cref="IODataPathHandler">OData path handler</see> to use for parsing the OData path.</param>
        /// <param name="routingConventions">The <see cref="IEnumerable{T}">sequence</see> of <see cref="IODataRoutingConvention">OData routing conventions</see>
        /// to use for controller and action selection.</param>
        /// <param name="batchHandler">The <see cref="ODataBatchHandler">OData batch handler</see>.</param>
        /// <returns>The mapped <see cref="ODataRoute">OData route</see>.</returns>
        /// <remarks>The <see cref="ApiVersionAnnotation">API version annotation</see> will be added or updated on the specified <paramref name="model"/> using
        /// the provided <paramref name="apiVersion">API version</paramref>.</remarks>
        public static ODataRoute MapVersionedODataRoute(
            this IRouteBuilder builder,
            string routeName,
            string routePrefix,
            IEdmModel model,
            ApiVersion apiVersion,
            IODataPathHandler pathHandler,
            IEnumerable <IODataRoutingConvention> routingConventions,
            ODataBatchHandler batchHandler)
        {
            Arg.NotNull(builder, nameof(builder));
            Arg.NotNullOrEmpty(routeName, nameof(routeName));
            Arg.NotNull(model, nameof(model));
            Arg.NotNull(apiVersion, nameof(apiVersion));
            Contract.Ensures(Contract.Result <ODataRoute>() != null);

            IEnumerable <IODataRoutingConvention> NewRoutingConventions(IServiceProvider serviceProvider)
            {
                var conventions = VersionedODataRoutingConventions.AddOrUpdate(routingConventions.ToList());

                conventions.Insert(0, new VersionedAttributeRoutingConvention(routeName, builder.ServiceProvider, apiVersion));
                return(conventions.ToArray());
            }

            var routeCollection          = builder.ServiceProvider.GetRequiredService <IODataRouteCollectionProvider>();
            var perRouteContainer        = builder.ServiceProvider.GetRequiredService <IPerRouteContainer>();
            var options                  = builder.ServiceProvider.GetRequiredService <ODataOptions>();
            var inlineConstraintResolver = builder.ServiceProvider.GetRequiredService <IInlineConstraintResolver>();

            if (pathHandler != null && pathHandler.UrlKeyDelimiter == null)
            {
                pathHandler.UrlKeyDelimiter = options.UrlKeyDelimiter;
            }

            model.SetAnnotationValue(model, new ApiVersionAnnotation(apiVersion));

            var configureAction = builder.ConfigureDefaultServices(container =>
                                                                   container.AddService(Singleton, typeof(IEdmModel), sp => model)
                                                                   .AddService(Singleton, typeof(IODataPathHandler), sp => pathHandler)
                                                                   .AddService(Singleton, typeof(IEnumerable <IODataRoutingConvention>), NewRoutingConventions)
                                                                   .AddService(Singleton, typeof(ODataBatchHandler), sp => batchHandler));
            var rootContainer   = perRouteContainer.CreateODataRootContainer(routeName, configureAction);
            var router          = rootContainer.GetService <IRouter>() ?? builder.DefaultHandler;
            var routeConstraint = new VersionedODataPathRouteConstraint(routeName, apiVersion);
            var route           = new ODataRoute(router, routeName, routePrefix.RemoveTrailingSlash(), routeConstraint, inlineConstraintResolver);

            builder.ConfigureBatchHandler(rootContainer, route);
            builder.Routes.Add(route);
            routeCollection.Add(new ODataRouteMapping(route, apiVersion, rootContainer));
            builder.AddRouteToRespondWithBadRequestWhenAtLeastOneRouteCouldMatch(routeName, routePrefix, apiVersion, inlineConstraintResolver);
            NotifyRoutesMapped();

            return(route);
        }
 /// <summary>
 /// Maps the specified OData route and the OData route attributes. When the <paramref name="batchHandler"/> is
 /// non-<c>null</c>, it will create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="builder">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="model">The EDM model to use for parsing OData paths.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The <see cref="IEndpointRouteBuilder"/>.</returns>
 public static IEndpointRouteBuilder MapODataRoute(this IEndpointRouteBuilder builder,
                                                   string routeName,
                                                   string routePrefix,
                                                   IEdmModel model,
                                                   ODataBatchHandler batchHandler)
 {
     return(builder.MapODataRoute(routeName, routePrefix, containerBuilder =>
                                  containerBuilder.AddService(ServiceLifetime.Singleton, sp => model)
                                  .AddService(ServiceLifetime.Singleton, sp => batchHandler)
                                  .AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp =>
                                                                                       ODataRoutingConventions.CreateDefaultWithAttributeRouting(routeName, builder.ServiceProvider))));
 }
Example #14
0
 /// <summary>
 /// Maps the specified OData route and the OData route attributes. When the <paramref name="batchHandler"/> is
 /// non-<c>null</c>, it will create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="builder">The <see cref="IRouteBuilder"/> to add the route to.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="models">The <see cref="IEnumerable{T}">sequence</see> of <see cref="IEdmModel">EDM models</see> to use for parsing OData paths.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The added <see cref="ODataRoute"/>.</returns>
 public static ODataRoute MapVersionedODataRoute(
     this IRouteBuilder builder,
     string routeName,
     string routePrefix,
     IEnumerable <IEdmModel> models,
     ODataBatchHandler batchHandler) =>
 AddRoute(
     builder,
     builder.MapODataServiceRoute(
         routeName,
         routePrefix,
         container => container.AddApiVersioning(routeName, models, builder.ServiceProvider)
         .AddService(Singleton, sp => batchHandler)));
 /// <summary>
 /// Maps the specified OData route and the OData route attributes. When the <paramref name="batchHandler"/> is
 /// non-<c>null</c>, it will create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="configuration">The server configuration.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="models">The <see cref="IEnumerable{T}">sequence</see> of <see cref="IEdmModel">EDM models</see> to use for parsing OData paths.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The added <see cref="ODataRoute"/>.</returns>
 public static ODataRoute MapVersionedODataRoute(
     this HttpConfiguration configuration,
     string routeName,
     string routePrefix,
     IEnumerable <IEdmModel> models,
     ODataBatchHandler batchHandler) =>
 AddApiVersionConstraintIfNecessary(
     configuration,
     configuration.MapODataServiceRoute(
         routeName,
         routePrefix,
         builder => builder.AddApiVersioning(routeName, models)
         .AddService(Singleton, sp => batchHandler)));
 /// <summary>
 /// Maps the specified OData route. When the <paramref name="batchHandler"/> is non-<c>null</c>, it will
 /// create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="builder">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="model">The EDM model to use for parsing OData paths.</param>
 /// <param name="pathHandler">The <see cref="IODataPathHandler" /> to use for parsing the OData path.</param>
 /// <param name="routingConventions">
 /// The OData routing conventions to use for controller and action selection.
 /// </param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The <see cref="IEndpointRouteBuilder"/>.</returns>
 public static IEndpointRouteBuilder MapODataRoute(this IEndpointRouteBuilder builder,
                                                   string routeName,
                                                   string routePrefix,
                                                   IEdmModel model,
                                                   IODataPathHandler pathHandler,
                                                   IEnumerable <IODataRoutingConvention> routingConventions,
                                                   ODataBatchHandler batchHandler)
 {
     return(builder.MapODataRoute(routeName, routePrefix, containerBuilder =>
                                  containerBuilder.AddService(ServiceLifetime.Singleton, sp => model)
                                  .AddService(ServiceLifetime.Singleton, sp => pathHandler)
                                  .AddService(ServiceLifetime.Singleton, sp => routingConventions.ToList().AsEnumerable())
                                  .AddService(ServiceLifetime.Singleton, sp => batchHandler)));
 }
Example #17
0
        /// <summary>
        /// Maps the specified OData route. When the <paramref name="defaultHandler"/> is non-<c>null</c>, it will map
        /// it as the handler for the route.
        /// </summary>
        /// <param name="configuration">The server configuration.</param>
        /// <param name="routeName">The name of the route to map.</param>
        /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
        /// <param name="model">The EDM model to use for parsing OData paths.</param>
        /// <param name="pathHandler">The <see cref="IODataPathHandler" /> to use for parsing the OData path.</param>
        /// <param name="routingConventions">
        /// The OData routing conventions to use for controller and action selection.
        /// </param>
        /// <param name="defaultHandler">The default <see cref="HttpMessageHandler"/> for this route.</param>
        /// <returns>The added <see cref="ODataRoute"/>.</returns>
        public static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
                                                      string routePrefix, IEdmModel model, IODataPathHandler pathHandler,
                                                      IEnumerable <IODataRoutingConvention> routingConventions, HttpMessageHandler defaultHandler)
        {
            if (configuration == null)
            {
                throw Error.ArgumentNull("configuration");
            }

            // We have a more specific overload to map batch handlers that creates a different route for the batch
            // endpoint instead of mapping that handler as the per route handler. Given that HttpMessageHandler is a
            // base type of ODataBatchHandler, it's possible the compiler will call this overload instead of the one
            // for the batch handler, so we detect that case and call the appropiate overload for the user.
            // The case in which the compiler picks the wrong overload is:
            // HttpRequestMessageHandler batchHandler = new DefaultODataBatchHandler(httpServer);
            // config.Routes.MapODataServiceRoute("routeName", "routePrefix", model, batchHandler);
            if (defaultHandler != null)
            {
                ODataBatchHandler batchHandler = defaultHandler as ODataBatchHandler;
                if (batchHandler != null)
                {
                    return(MapODataServiceRoute(configuration, routeName, routePrefix, model, batchHandler));
                }
            }

            HttpRouteCollection routes = configuration.Routes;

            routePrefix = RemoveTrailingSlash(routePrefix);

            DefaultODataPathHandler odataPathHandler = pathHandler as DefaultODataPathHandler;

            if (odataPathHandler != null)
            {
                odataPathHandler.ResolverSetttings = configuration.GetResolverSettings();
            }

            ODataPathRouteConstraint routeConstraint =
                new ODataPathRouteConstraint(pathHandler, model, routeName, routingConventions);
            ODataRoute route = new ODataRoute(
                routePrefix,
                routeConstraint,
                defaults: null,
                constraints: null,
                dataTokens: null,
                handler: defaultHandler);

            routes.Add(routeName, route);
            return(route);
        }
        static void Configure(this ODataBatchHandler batchHandler, IRouteBuilder builder, ODataRoute route)
        {
            batchHandler.ODataRoute     = route;
            batchHandler.ODataRouteName = route.Name;

            var batchPath = '/' + ODataRouteConstants.Batch;

            if (!IsNullOrEmpty(route.RoutePrefix))
            {
                batchPath = '/' + route.RoutePrefix + batchPath;
            }

            var batchMapping = builder.ServiceProvider.GetRequiredService <ODataBatchPathMapping>();

            batchMapping.AddRoute(route.Name, batchPath);
        }
Example #19
0
        public static ODataRoute MapVersionedODataRoute(
            this HttpConfiguration configuration,
            string routeName,
            string routePrefix,
            IEdmModel model,
            ApiVersion apiVersion,
            IODataPathHandler pathHandler,
            IEnumerable<IODataRoutingConvention> routingConventions,
            ODataBatchHandler batchHandler )
        {
            Arg.NotNull( configuration, nameof( configuration ) );
            Arg.NotNull( model, nameof( model ) );
            Arg.NotNull( apiVersion, nameof( apiVersion ) );
            Contract.Ensures( Contract.Result<ODataRoute>() != null );

            var routeConventions = EnsureConventions( routingConventions.ToList() );
            var routes = configuration.Routes;

            if ( !IsNullOrEmpty( routePrefix ) )
            {
                routePrefix = routePrefix.TrimEnd( '/' );
            }

            if ( batchHandler != null )
            {
                var batchTemplate = IsNullOrEmpty( routePrefix ) ? ODataRouteConstants.Batch : routePrefix + '/' + ODataRouteConstants.Batch;
                routes.MapHttpBatchRoute( routeName + "Batch", batchTemplate, batchHandler );
            }

            configuration.SetResolverSettings( pathHandler );
            model.SetAnnotationValue( model, new ApiVersionAnnotation( apiVersion ) );
            routeConventions.Insert( 0, new VersionedAttributeRoutingConvention( model, configuration ) );

            var routeConstraint = new VersionedODataPathRouteConstraint( pathHandler, model, routeName, routeConventions.ToArray(), apiVersion );
            var route = new ODataRoute( routePrefix, routeConstraint );

            AddApiVersionConstraintIfNecessary( route );
            routes.Add( routeName, route );

            var unversionedRouteConstraint = new ODataPathRouteConstraint( pathHandler, model, routeName, routeConventions.ToArray() );
            var unversionedRoute = new ODataRoute( routePrefix, new UnversionedODataPathRouteConstraint( unversionedRouteConstraint, apiVersion ) );

            AddApiVersionConstraintIfNecessary( unversionedRoute );
            routes.Add( routeName + UnversionedRouteSuffix, unversionedRoute );

            return route;
        }
Example #20
0
        /// <summary>
        /// Instructs WebApi to map one or more of the registered Restier APIs to the specified Routes, each with it's own isolated Dependency Injection container.
        /// </summary>
        /// <param name="config">The <see cref="HttpConfiguration"/> instance to enhance.</param>
        /// <param name="configureRoutesAction">The action for configuring a set of routes.</param>
        /// <param name="httpServer">The HttpServer instance to create the routes on.</param>
        /// <returns>The <see cref="HttpConfiguration"/> instance to allow for fluent method chaining.</returns>
        /// <example>
        /// <code>
        /// config.MapRestier(builder =>
        ///     builder
        ///         .MapApiRoute<SomeApi>("SomeApiV1", "someapi/")
        ///         .MapApiRoute<AnotherApi>("AnotherApiV1", "anotherapi/")
        /// );
        /// </code>
        /// </example>
        public static HttpConfiguration MapRestier(this HttpConfiguration config, Action <RestierRouteBuilder> configureRoutesAction, HttpServer httpServer)
        {
            Ensure.NotNull(configureRoutesAction, nameof(configureRoutesAction));

            var rrb = new RestierRouteBuilder();

            configureRoutesAction.Invoke(rrb);

            foreach (var route in rrb.Routes)
            {
                ODataBatchHandler batchHandler = null;
                var conventions = CreateRestierRoutingConventions(config, route.Key);

                if (route.Value.AllowBatching)
                {
                    if (httpServer == null)
                    {
                        throw new ArgumentNullException(nameof(httpServer), OwinException);
                    }

#pragma warning disable IDE0067 // Dispose objects before losing scope
                    batchHandler = new RestierBatchHandler(httpServer)
                    {
                        ODataRouteName = route.Key
                    };
#pragma warning restore IDE0067 // Dispose objects before losing scope
                }

                var odataRoute = config.MapODataServiceRoute(route.Key, route.Value.RoutePrefix, (containerBuilder, routeName) =>
                {
                    var rcb          = containerBuilder as RestierContainerBuilder;
                    rcb.routeBuilder = rrb;
                    rcb.RouteName    = routeName;

                    containerBuilder.AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp => conventions);
                    if (batchHandler != null)
                    {
                        //RWM: DO NOT simplify this generic signature. It HAS to stay this way, otherwise the code breaks.
                        containerBuilder.AddService <ODataBatchHandler>(ServiceLifetime.Singleton, sp => batchHandler);
                    }
                });
            }

            return(config);
        }
 /// <summary>
 /// Maps the specified OData route. When the <paramref name="batchHandler"/> is non-<c>null</c>, it will
 /// create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="builder">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="models">The <see cref="IEnumerable{T}">sequence</see> of <see cref="IEdmModel">EDM models</see> to use for parsing OData paths.</param>
 /// <param name="pathHandler">The <see cref="IODataPathHandler" /> to use for parsing the OData path.</param>
 /// <param name="routingConventions">
 /// The OData routing conventions to use for controller and action selection.
 /// </param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The <see cref="IEndpointRouteBuilder"/>.</returns>
 public static IEndpointRouteBuilder MapVersionedODataRoute(
     this IEndpointRouteBuilder builder,
     string routeName,
     string routePrefix,
     IEnumerable <IEdmModel> models,
     IODataPathHandler pathHandler,
     IEnumerable <IODataRoutingConvention> routingConventions,
     ODataBatchHandler batchHandler) =>
 AddRoute(
     builder.MapODataRoute(
         routeName,
         routePrefix,
         container =>
         container.AddApiVersioning(models, routingConventions, builder.ServiceProvider)
         .AddService(Singleton, sp => pathHandler)
         .AddService(Singleton, sp => batchHandler)),
     routeName,
     routePrefix);
        /// <summary>
        /// Instructs WebApi to map one or more of the registered Restier APIs to the specified Routes, each with it's own isolated Dependency Injection container.
        /// </summary>
        /// <param name="routeBuilder">The <see cref="HttpConfiguration"/> instance to enhance.</param>
        /// <param name="configureRoutesAction">The action for configuring a set of routes.</param>
        /// <returns>The <see cref="HttpConfiguration"/> instance to allow for fluent method chaining.</returns>
        /// <example>
        /// <code>
        /// config.MapRestier(builder =>
        ///     builder
        ///         .MapApiRoute<SomeApi>("SomeApiV1", "someapi/")
        ///         .MapApiRoute<AnotherApi>("AnotherApiV1", "anotherapi/")
        /// );
        /// </code>
        /// </example>
        public static IRouteBuilder MapRestier(this IRouteBuilder routeBuilder, Action <RestierRouteBuilder> configureRoutesAction)
        {
            Ensure.NotNull(routeBuilder, nameof(routeBuilder));
            Ensure.NotNull(configureRoutesAction, nameof(configureRoutesAction));

            var perRouteContainer = routeBuilder.ServiceProvider.GetRequiredService <IPerRouteContainer>();

            perRouteContainer.BuilderFactory = () => routeBuilder.ServiceProvider.GetRequiredService <IContainerBuilder>();

            var rrb = new RestierRouteBuilder();

            configureRoutesAction.Invoke(rrb);

            foreach (var route in rrb.Routes)
            {
                ODataBatchHandler batchHandler = null;

                if (route.Value.AllowBatching)
                {
#pragma warning disable IDE0067 // Dispose objects before losing scope
                    batchHandler = new RestierBatchHandler()
                    {
                        ODataRouteName = route.Key
                    };
#pragma warning restore IDE0067 // Dispose objects before losing scope
                }

                var odataRoute = routeBuilder.MapODataServiceRoute(route.Key, route.Value.RoutePrefix, (containerBuilder) =>
                {
                    var rcb          = containerBuilder as RestierContainerBuilder;
                    rcb.routeBuilder = rrb;
                    rcb.RouteName    = route.Key;

                    containerBuilder.AddService <IEnumerable <IODataRoutingConvention> >(OData.ServiceLifetime.Singleton, sp => routeBuilder.CreateRestierRoutingConventions(route.Key));
                    if (batchHandler != null)
                    {
                        //RWM: DO NOT simplify this generic signature. It HAS to stay this way, otherwise the code breaks.
                        containerBuilder.AddService <ODataBatchHandler>(OData.ServiceLifetime.Singleton, sp => batchHandler);
                    }
                });
            }

            return(routeBuilder);
        }
Example #23
0
        public void AddModel_WithBatchHandler_SetModel()
        {
            // Arrange
            ODataOptions      options  = new ODataOptions();
            IEdmModel         edmModel = EdmCoreModel.Instance;
            ODataBatchHandler handler  = new Mock <ODataBatchHandler>().Object;

            // Act
            options.AddModel(edmModel, handler);

            // Assert
            KeyValuePair <string, (IEdmModel, IServiceProvider)> model = Assert.Single(options.Models);

            Assert.Equal(String.Empty, model.Key);

            Assert.Same(edmModel, model.Value.Item1);
            Assert.NotNull(model.Value.Item2);
            ODataBatchHandler actual = model.Value.Item2.GetService <ODataBatchHandler>();

            Assert.Same(handler, actual);
        }
        private static ODataRoute CustomMapODataServiceRoute(
            HttpRouteCollection routes,
            string routeName,
            string routePrefix,
            Func <HttpRequestMessage, IEdmModel> modelProvider,
            IODataPathHandler pathHandler,
            IEnumerable <IODataRoutingConvention> routingConventions,
            ODataBatchHandler batchHandler)
        {
            if (!string.IsNullOrEmpty(routePrefix))
            {
                int prefixLastIndex = routePrefix.Length - 1;
                if (routePrefix[prefixLastIndex] == '/')
                {
                    routePrefix = routePrefix.Substring(0, routePrefix.Length - 1);
                }
            }

            if (batchHandler != null)
            {
                batchHandler.ODataRouteName = routeName;
                string batchTemplate = string.IsNullOrEmpty(routePrefix)
                    ? ODataRouteConstants.Batch
                    : routePrefix + '/' + ODataRouteConstants.Batch;
                routes.MapHttpBatchRoute(routeName + "Batch", batchTemplate, batchHandler);
            }

            CustomODataPathRouteConstraint routeConstraint = new CustomODataPathRouteConstraint(
                pathHandler,
                modelProvider,
                routeName,
                routingConventions);
            CustomODataRoute odataRoute = new CustomODataRoute(routePrefix, routeConstraint);

            routes.Add(routeName, odataRoute);

            return(odataRoute);
        }
        public virtual void Configure(IAppBuilder owinApp)
        {
            if (owinApp == null)
            {
                throw new ArgumentNullException(nameof(owinApp));
            }

            _webApiConfig = new HttpConfiguration();
            _webApiConfig.SuppressHostPrincipal();

            _webApiConfig.SetTimeZoneInfo(TimeZoneInfo.Utc);

            _webApiConfig.Formatters.Clear();

            _webApiConfig.IncludeErrorDetailPolicy = _activeAppEnvironment.DebugMode ? IncludeErrorDetailPolicy.LocalOnly : IncludeErrorDetailPolicy.Never;

            _webApiConfgurationCustomizers.ToList()
            .ForEach(webApiConfigurationCustomizer =>
            {
                webApiConfigurationCustomizer.CustomizeWebApiConfiguration(_webApiConfig);
            });

            _webApiConfig.DependencyResolver = _webApiDependencyResolver;

            _server = new HttpServer(_webApiConfig);

            foreach (IGrouping <string, IEdmModelProvider> edmModelProviders in _emdEdmModelProviders.GroupBy(mp => mp.GetEdmName()))
            {
                ODataModelBuilder modelBuilder = _oDataModelBuilderProvider.GetODataModelBuilder(_webApiConfig, containerName: $"{edmModelProviders.Key}Context", @namespace: edmModelProviders.Key);

                foreach (IEdmModelProvider edmModelProvider in edmModelProviders)
                {
                    edmModelProvider.BuildEdmModel(modelBuilder);
                }

                string routeName = $"{edmModelProviders.Key}-odata";

                _odataBatchHandler = new DefaultODataBatchHandler(_server);

                _odataBatchHandler.MessageQuotas.MaxOperationsPerChangeset = int.MaxValue;

                _odataBatchHandler.MessageQuotas.MaxPartsPerBatch = int.MaxValue;

                _odataBatchHandler.MessageQuotas.MaxNestingDepth = int.MaxValue;

                _odataBatchHandler.MessageQuotas.MaxReceivedMessageSize = long.MaxValue;

                _odataBatchHandler.ODataRouteName = routeName;

                IEnumerable <IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault();

                IEdmModel edmModel = modelBuilder.GetEdmModel();

                _webApiConfig.MapODataServiceRoute(routeName, edmModelProviders.Key, builder =>
                {
                    builder.AddService(ServiceLifetime.Singleton, sp => conventions);
                    builder.AddService(ServiceLifetime.Singleton, sp => edmModel);
                    builder.AddService(ServiceLifetime.Singleton, sp => _odataBatchHandler);
                    builder.AddService(ServiceLifetime.Singleton, sp => _webApiDependencyResolver);
                    _oDataContainerBuilderCustomizer.Customize(builder);
                });
            }

            owinApp.UseAutofacWebApi(_webApiConfig);

            _webApiOwinPipelineInjector.UseWebApiOData(owinApp, _server);

            _webApiConfig.EnsureInitialized();
        }
        /// <summary>
        /// Maps the specified OData route and the OData route attributes.
        /// </summary>
        /// <param name="builder">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
        /// <param name="routeName">The name of the route to map.</param>
        /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
        /// <param name="configureAction">The configuring action to add the services to the root container.</param>
        /// <returns>The input <see cref="IEndpointRouteBuilder"/>.</returns>
        public static IEndpointRouteBuilder MapODataRoute(this IEndpointRouteBuilder builder,
                                                          string routeName,
                                                          string routePrefix,
                                                          Action <IContainerBuilder> configureAction)
        {
            if (builder == null)
            {
                throw Error.ArgumentNull("builder");
            }

            if (routeName == null)
            {
                throw Error.ArgumentNull("routeName");
            }

            // Build and configure the root container.
            IServiceProvider serviceProvider = builder.ServiceProvider;

            IPerRouteContainer perRouteContainer = serviceProvider.GetRequiredService <IPerRouteContainer>();

            if (perRouteContainer == null)
            {
                throw Error.InvalidOperation(SRResources.MissingODataServices, nameof(IPerRouteContainer));
            }

            // Make sure the MetadataController is registered with the ApplicationPartManager.
            ApplicationPartManager applicationPartManager = serviceProvider.GetRequiredService <ApplicationPartManager>();

            applicationPartManager.ApplicationParts.Add(new AssemblyPart(typeof(MetadataController).Assembly));

            // Create an service provider for this route. Add the default services to the custom configuration actions.
            Action <IContainerBuilder> builderAction = ConfigureDefaultServices(builder, configureAction);

            IServiceProvider subServiceProvider = perRouteContainer.CreateODataRootContainer(routeName, builderAction);

            // Resolve the path handler and set URI resolver to it.
            IODataPathHandler pathHandler = subServiceProvider.GetRequiredService <IODataPathHandler>();

            // If settings is not on local, use the global configuration settings.
            ODataOptions options = serviceProvider.GetRequiredService <ODataOptions>();

            if (pathHandler != null && pathHandler.UrlKeyDelimiter == null)
            {
                pathHandler.UrlKeyDelimiter = options.UrlKeyDelimiter;
            }

            // Resolve HTTP handler, create the OData route and register it.
            routePrefix = RemoveTrailingSlash(routePrefix);

            // If a batch handler is present, register the route with the batch path mapper. This will be used
            // by the batching middleware to handle the batch request. Batching still requires the injection
            // of the batching middleware via UseODataBatching().
            ODataBatchHandler batchHandler = subServiceProvider.GetService <ODataBatchHandler>();

            if (batchHandler != null)
            {
                // TODO: for the $batch, i need more time to refactor/test it.
                // batchHandler.ODataRoute = route;
                batchHandler.ODataRouteName = routeName;

                string batchPath = String.IsNullOrEmpty(routePrefix)
                    ? '/' + ODataRouteConstants.Batch
                    : '/' + routePrefix + '/' + ODataRouteConstants.Batch;

                ODataBatchPathMapping batchMapping = builder.ServiceProvider.GetRequiredService <ODataBatchPathMapping>();
                batchMapping.AddRoute(routeName, batchPath);
            }

            builder.MapDynamicControllerRoute <ODataEndpointRouteValueTransformer>(
                ODataEndpointPattern.CreateODataEndpointPattern(routeName, routePrefix));

            perRouteContainer.AddRoute(routeName, routePrefix);

            return(builder);
        }
Example #27
0
        public static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
                                                      string routePrefix, IEdmModel model, IODataPathHandler pathHandler,
                                                      IEnumerable <IODataRoutingConvention> routingConventions, ODataBatchHandler batchHandler)
        {
            if (configuration == null)
            {
                throw Error.ArgumentNull("configuration");
            }

            HttpRouteCollection routes = configuration.Routes;

            routePrefix = RemoveTrailingSlash(routePrefix);

            if (batchHandler != null)
            {
                batchHandler.ODataRouteName = routeName;
                string batchTemplate = String.IsNullOrEmpty(routePrefix) ? ODataRouteConstants.Batch
                    : routePrefix + '/' + ODataRouteConstants.Batch;
                routes.MapHttpBatchRoute(routeName + "Batch", batchTemplate, batchHandler);
            }

            DefaultODataPathHandler odataPathHandler = pathHandler as DefaultODataPathHandler;

            if (odataPathHandler != null)
            {
                odataPathHandler.ResolverSetttings = configuration.GetResolverSettings();
            }

            ODataPathRouteConstraint routeConstraint =
                new ODataPathRouteConstraint(pathHandler, model, routeName, routingConventions);
            ODataRoute route = new ODataRoute(routePrefix, routeConstraint);

            routes.Add(routeName, route);
            return(route);
        }
Example #28
0
 /// <summary>
 /// Maps the specified OData route and the OData route attributes. When the <paramref name="batchHandler"/> is
 /// non-<c>null</c>, it will create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="configuration">The server configuration.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="model">The EDM model to use for parsing OData paths.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The added <see cref="ODataRoute"/>.</returns>
 public static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
                                               string routePrefix, IEdmModel model, ODataBatchHandler batchHandler)
 {
     return(MapODataServiceRoute(configuration, routeName, routePrefix, model, new DefaultODataPathHandler(),
                                 ODataRoutingConventions.CreateDefaultWithAttributeRouting(configuration, model), batchHandler));
 }
 public static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
                                               string routePrefix, IEdmModel model, IODataPathHandler pathHandler,
                                               IEnumerable <IODataRoutingConvention> routingConventions, ODataBatchHandler batchHandler)
 {
     return(configuration.MapODataServiceRoute(routeName, routePrefix, builder =>
                                               builder.AddService(ServiceLifetime.Singleton, sp => model)
                                               .AddService(ServiceLifetime.Singleton, sp => pathHandler)
                                               .AddService(ServiceLifetime.Singleton, sp => routingConventions.ToList().AsEnumerable())
                                               .AddService(ServiceLifetime.Singleton, sp => batchHandler)));
 }
 /// <summary>
 /// Maps the specified OData route and the OData route attributes. When the <paramref name="batchHandler"/> is
 /// non-<c>null</c>, it will create a '$batch' endpoint to handle the batch requests.
 /// </summary>
 /// <param name="configuration">The server configuration.</param>
 /// <param name="routeName">The name of the route to map.</param>
 /// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
 /// <param name="model">The EDM model to use for parsing OData paths.</param>
 /// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
 /// <returns>The added <see cref="ODataRoute"/>.</returns>
 public static ODataRoute MapODataServiceRoute(this HttpConfiguration configuration, string routeName,
                                               string routePrefix, IEdmModel model, ODataBatchHandler batchHandler)
 {
     return(configuration.MapODataServiceRoute(routeName, routePrefix, builder =>
                                               builder.AddService(ServiceLifetime.Singleton, sp => model)
                                               .AddService(ServiceLifetime.Singleton, sp => batchHandler)
                                               .AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp =>
                                                                                                    ODataRoutingConventions.CreateDefaultWithAttributeRouting(routeName, configuration))));
 }