Skip to content

Commit 30571bc

Browse files
author
Chris Martinez
committed
Refactored GetEdmModels to use IHttpControllerTypeResolver instead of IHttpControllerSelector, which prevents a recursive call into a Lazy<T> initializer in some cases. API version discovery is also now filtered to only OData controllers.
1 parent 59d572b commit 30571bc

File tree

4 files changed

+51
-34
lines changed

4 files changed

+51
-34
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Microsoft
2+
{
3+
using System;
4+
using System.Web.OData;
5+
6+
internal static class TypeExtensions
7+
{
8+
internal static bool IsODataController( this Type controllerType ) => typeof( ODataController ).IsAssignableFrom( controllerType );
9+
}
10+
}

src/Microsoft.AspNet.OData.Versioning/Web.OData/Builder/VersionedODataModelBuilder.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
/// </summary>
1818
public class VersionedODataModelBuilder
1919
{
20-
private readonly IHttpControllerSelector controllerSelector;
20+
private readonly HttpConfiguration configuration;
2121
private Func<ODataModelBuilder> modelBuilderFactory = () => new ODataConventionModelBuilder();
2222

2323
/// <summary>
@@ -31,17 +31,7 @@ public class VersionedODataModelBuilder
3131
public VersionedODataModelBuilder( HttpConfiguration configuration )
3232
{
3333
Arg.NotNull( configuration, nameof( configuration ) );
34-
controllerSelector = configuration.Services.GetHttpControllerSelector();
35-
}
36-
37-
/// <summary>
38-
/// Initializes a new instance of the <see cref="VersionedODataModelBuilder"/>
39-
/// </summary>
40-
/// <param name="controllerSelector">The <see cref="IHttpControllerSelector">controller selector</see> associated with the builder.</param>
41-
public VersionedODataModelBuilder( IHttpControllerSelector controllerSelector )
42-
{
43-
Arg.NotNull( controllerSelector, nameof( controllerSelector ) );
44-
this.controllerSelector = controllerSelector;
34+
this.configuration = configuration;
4535
}
4636

4737
/// <summary>
@@ -107,9 +97,13 @@ public virtual IEnumerable<IEdmModel> GetEdmModels()
10797
{
10898
var configurations = GetMergedConfigurations();
10999
var models = new List<IEdmModel>();
110-
var apiVersions = ( from mapping in controllerSelector.GetControllerMapping().Values
111-
let descriptorGroup = mapping as IEnumerable<HttpControllerDescriptor> ?? new[] { mapping }
112-
from descriptor in descriptorGroup
100+
var services = configuration.Services;
101+
var assembliesResolver = services.GetAssembliesResolver();
102+
var typeResolver = services.GetHttpControllerTypeResolver();
103+
var controllerTypes = typeResolver.GetControllerTypes( assembliesResolver );
104+
var apiVersions = ( from controllerType in controllerTypes
105+
where controllerType.IsODataController()
106+
let descriptor = new HttpControllerDescriptor( configuration, string.Empty, controllerType )
113107
from version in descriptor.GetImplementedApiVersions()
114108
select version ).Distinct();
115109

test/Microsoft.AspNet.OData.Versioning.Tests/System.Web.OData/HttpConfigurationExtensionsTest.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
namespace System.Web.OData
22
{
3-
using FluentAssertions;
43
using Batch;
54
using Builder;
65
using Collections.Generic;
7-
using Collections.ObjectModel;
6+
using FluentAssertions;
87
using Http;
9-
using Http.Controllers;
108
using Http.Dispatcher;
119
using Linq;
1210
using Microsoft.OData.Edm;
@@ -19,16 +17,25 @@
1917

2018
public class HttpConfigurationExtensionsTest
2119
{
20+
[ApiVersion( "1.0" )]
21+
private sealed class ControllerV1 : ODataController
22+
{
23+
}
24+
25+
[ApiVersion( "2.0" )]
26+
private sealed class ControllerV2 : ODataController
27+
{
28+
}
29+
2230
private static IEnumerable<IEdmModel> CreateModels( HttpConfiguration configuration )
2331
{
24-
var controllerDescriptor = new Mock<HttpControllerDescriptor>( configuration, "Test", typeof( IHttpController ) ) { CallBase = true };
25-
var controllerMapping = new Dictionary<string, HttpControllerDescriptor>() { { "Test", controllerDescriptor.Object } };
26-
var controllerSelector = new Mock<IHttpControllerSelector>();
27-
var apiVersions = new Collection<ApiVersionAttribute>( new[] { new ApiVersionAttribute( "1.0" ), new ApiVersionAttribute( "2.0" ) } );
28-
var builder = new VersionedODataModelBuilder( controllerSelector.Object );
32+
var controllerTypeResolver = new Mock<IHttpControllerTypeResolver>();
33+
var controllerTypes = new List<Type>() { typeof( ControllerV1 ), typeof( ControllerV2 ) };
34+
35+
controllerTypeResolver.Setup( ctr => ctr.GetControllerTypes( It.IsAny<IAssembliesResolver>() ) ).Returns( controllerTypes );
36+
configuration.Services.Replace( typeof( IHttpControllerTypeResolver ), controllerTypeResolver.Object );
2937

30-
controllerDescriptor.Setup( cd => cd.GetCustomAttributes<ApiVersionAttribute>( It.IsAny<bool>() ) ).Returns( apiVersions );
31-
controllerSelector.Setup( cs => cs.GetControllerMapping() ).Returns( controllerMapping );
38+
var builder = new VersionedODataModelBuilder( configuration );
3239

3340
return builder.GetEdmModels();
3441
}
@@ -51,7 +58,7 @@ public void map_versioned_odata_routes_should_return_expected_result()
5158
var batchRoute = configuration.Routes["odataBatch"];
5259

5360
// assert
54-
constraint.RoutingConventions[0].Should().BeOfType<AttributeRoutingConvention>();
61+
constraint.RoutingConventions[0].Should().BeOfType<VersionedAttributeRoutingConvention>();
5562
constraint.RoutingConventions[1].Should().BeOfType<VersionedMetadataRoutingConvention>();
5663
constraint.RoutingConventions.OfType<MetadataRoutingConvention>().Should().BeEmpty();
5764
constraint.RouteName.Should().Be( routeName );
@@ -82,7 +89,7 @@ public void map_versioned_odata_routes_should_return_expected_results()
8289
var apiVersion = constraint.EdmModel.GetAnnotationValue<ApiVersionAnnotation>( constraint.EdmModel ).ApiVersion;
8390
var versionedRouteName = routeName + "-" + apiVersion.ToString();
8491

85-
constraint.RoutingConventions[0].Should().BeOfType<AttributeRoutingConvention>();
92+
constraint.RoutingConventions[0].Should().BeOfType<VersionedAttributeRoutingConvention>();
8693
constraint.RoutingConventions[1].Should().BeOfType<VersionedMetadataRoutingConvention>();
8794
constraint.RoutingConventions.OfType<MetadataRoutingConvention>().Should().BeEmpty();
8895
constraint.RouteName.Should().Be( versionedRouteName );

test/Microsoft.AspNet.OData.Versioning.Tests/Web.OData/Builder/VersionedODataModelBuilderTest.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,38 @@
88
using System.Collections.Generic;
99
using System.Linq;
1010
using System.Web.Http;
11-
using System.Web.Http.Controllers;
1211
using System.Web.Http.Dispatcher;
12+
using System.Web.OData;
1313
using System.Web.OData.Builder;
1414
using Xunit;
1515

1616
public class VersionedODataModelBuilderTest
1717
{
18+
[ApiVersion( "1.0" )]
19+
private sealed class ControllerV1 : ODataController
20+
{
21+
}
22+
1823
[Fact]
1924
public void get_edm_models_should_return_expected_results()
2025
{
2126
// arrange
2227
var configuration = new HttpConfiguration();
23-
var controllerDescriptor = new HttpControllerDescriptor( configuration, "Test", typeof( IHttpController ) );
24-
var controllerMapping = new Dictionary<string, HttpControllerDescriptor>() { { "Test", controllerDescriptor } };
25-
var controllerSelector = new Mock<IHttpControllerSelector>();
28+
var controllerTypeResolver = new Mock<IHttpControllerTypeResolver>();
29+
var controllerTypes = new List<Type>() { typeof( ControllerV1 ) };
30+
31+
controllerTypeResolver.Setup( ctr => ctr.GetControllerTypes( It.IsAny<IAssembliesResolver>() ) ).Returns( controllerTypes );
32+
configuration.Services.Replace( typeof( IHttpControllerTypeResolver ), controllerTypeResolver.Object );
33+
2634
var defaultConfiguration = new Mock<Action<ODataModelBuilder, ApiVersion>>();
2735
var modelCreated = new Mock<Action<ODataModelBuilder, IEdmModel>>();
2836
var apiVersion = new ApiVersion( 1, 0 );
29-
var builder = new VersionedODataModelBuilder( controllerSelector.Object )
37+
var builder = new VersionedODataModelBuilder( configuration )
3038
{
3139
DefaultModelConfiguration = defaultConfiguration.Object,
3240
OnModelCreated = modelCreated.Object
3341
};
3442

35-
controllerSelector.Setup( cs => cs.GetControllerMapping() ).Returns( controllerMapping );
36-
3743
// act
3844
var model = builder.GetEdmModels().Single();
3945

0 commit comments

Comments
 (0)