Skip to content

Commit 4a6ca43

Browse files
committed
change to lazy loading for the validation visitors
1 parent da5f90a commit 4a6ca43

File tree

2 files changed

+112
-32
lines changed

2 files changed

+112
-32
lines changed

src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using Microsoft.OpenApi.Exceptions;
78
using Microsoft.OpenApi.Models;
89
using Microsoft.OpenApi.Properties;
10+
using Microsoft.OpenApi.Interfaces;
911

1012
namespace Microsoft.OpenApi.Validations.Visitors
1113
{
@@ -14,39 +16,23 @@ namespace Microsoft.OpenApi.Validations.Visitors
1416
/// </summary>
1517
internal static class OpenApiVisitorSet
1618
{
17-
private static IDictionary<Type, IVisitor> _visitorCache = new Dictionary<Type, IVisitor>
19+
private static IDictionary<Type, IVisitor> _visitors;
20+
21+
/// <summary>
22+
/// Gets the visitors
23+
/// </summary>
24+
public static IDictionary<Type, IVisitor> Visitors
1825
{
19-
{ typeof(OpenApiCallback), new CallbackVisitor() },
20-
{ typeof(OpenApiComponents), new ComponentsVisitor() },
21-
{ typeof(OpenApiContact), new ContactVisitor() },
22-
{ typeof(OpenApiDiscriminator), new DiscriminatorVisitor() },
23-
{ typeof(OpenApiDocument), new DocumentVisitor() },
24-
{ typeof(OpenApiEncoding), new EncodingVisitor() },
25-
{ typeof(OpenApiExample), new ExampleVisitor() },
26-
{ typeof(OpenApiExternalDocs), new ExternalDocsVisitor() },
27-
{ typeof(OpenApiHeader), new HeaderVisitor() },
28-
{ typeof(OpenApiInfo), new InfoVisitor() },
29-
{ typeof(OpenApiLicense), new LicenseVisitor() },
30-
{ typeof(OpenApiLink), new LinkVisitor() },
31-
{ typeof(OpenApiMediaType), new MediaTypeVisitor() },
32-
{ typeof(OpenApiOAuthFlows), new OAuthFlowsVisitor() },
33-
{ typeof(OpenApiOAuthFlow), new OAuthFlowVisitor() },
34-
{ typeof(OpenApiOperation), new OperationVisitor() },
35-
{ typeof(OpenApiParameter), new ParameterVisitor() },
36-
{ typeof(OpenApiPathItem), new PathItemVisitor() },
37-
{ typeof(OpenApiPaths), new PathsVisitor() },
38-
{ typeof(OpenApiRequestBody), new RequestBodyVisitor() },
39-
{ typeof(OpenApiResponses), new ResponsesVisitor() },
40-
{ typeof(OpenApiResponse), new ResponseVisitor() },
41-
{ typeof(OpenApiSchema), new SchemaVisitor() },
42-
{ typeof(OpenApiSecurityRequirement), new SecurityRequirementVisitor() },
43-
{ typeof(OpenApiSecurityScheme), new SecuritySchemeVisitor() },
44-
{ typeof(OpenApiServerVariable), new ServerVariableVisitor() },
45-
{ typeof(OpenApiServer), new ServerVisitor() },
46-
{ typeof(OpenApiTag), new TagVisitor() },
47-
{ typeof(OpenApiXml), new XmlVisitor() }
48-
};
26+
get
27+
{
28+
if (_visitors == null)
29+
{
30+
_visitors = new Lazy<IDictionary<Type, IVisitor>>(() => BuildVisitorSet(), isThreadSafe: false).Value;
31+
}
4932

33+
return _visitors;
34+
}
35+
}
5036
/// <summary>
5137
/// Get the element visitor.
5238
/// </summary>
@@ -55,12 +41,51 @@ internal static class OpenApiVisitorSet
5541
public static IVisitor GetVisitor(Type elementType)
5642
{
5743
IVisitor visitor;
58-
if (_visitorCache.TryGetValue(elementType, out visitor))
44+
if (Visitors.TryGetValue(elementType, out visitor))
5945
{
6046
return visitor;
6147
}
6248

6349
throw new OpenApiException(String.Format(SRResource.UnknownVisitorType, elementType.FullName));
6450
}
51+
52+
private static IDictionary<Type, IVisitor> BuildVisitorSet()
53+
{
54+
IDictionary<Type, IVisitor> visitors = new Dictionary<Type, IVisitor>();
55+
56+
IEnumerable<Type> allTypes = typeof(OpenApiVisitorSet).Assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t != typeof(object));
57+
58+
Type visitorInterfaceType = typeof(IVisitor);
59+
Type elementType = typeof(IOpenApiElement);
60+
foreach (Type type in allTypes)
61+
{
62+
if (!visitorInterfaceType.IsAssignableFrom(type))
63+
{
64+
continue;
65+
}
66+
67+
Type baseType = type.BaseType;
68+
if (baseType == null || !baseType.IsGenericType ||
69+
baseType.GetGenericTypeDefinition() != typeof(VisitorBase<>))
70+
{
71+
continue;
72+
}
73+
74+
Type validationType = baseType.GetGenericArguments()[0];
75+
if (!elementType.IsAssignableFrom(validationType))
76+
{
77+
continue;
78+
}
79+
80+
object instance = Activator.CreateInstance(type);
81+
IVisitor visitor = instance as IVisitor;
82+
if (visitor != null)
83+
{
84+
visitors[validationType] = visitor;
85+
}
86+
}
87+
88+
return visitors;
89+
}
6590
}
6691
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using Microsoft.OpenApi.Exceptions;
6+
using Microsoft.OpenApi.Models;
7+
using Xunit;
8+
9+
namespace Microsoft.OpenApi.Validations.Visitors.Tests
10+
{
11+
public class OpenApiVisitorSetTests
12+
{
13+
[Fact]
14+
public void VisitorsPropertyReturnsTheCorrectVisitorList()
15+
{
16+
for (int i = 0; i < 5; i++) // 5 is just a magic number
17+
{
18+
// Arrange & Act
19+
var visitors = OpenApiVisitorSet.Visitors;
20+
21+
// Assert
22+
Assert.NotNull(visitors);
23+
Assert.NotEmpty(visitors);
24+
Assert.Equal(29, visitors.Count);
25+
}
26+
}
27+
28+
[Fact]
29+
public void GetVisitorThrowsUnknowElementType()
30+
{
31+
// Arrange & Act
32+
Action test = () => OpenApiVisitorSet.GetVisitor(typeof(OpenApiVisitorSetTests));
33+
34+
// Assert
35+
var exception = Assert.Throws<OpenApiException>(test);
36+
Assert.Equal("Can not find visitor type registered for type 'Microsoft.OpenApi.Validations.Visitors.Tests.OpenApiVisitorSetTests'.",
37+
exception.Message);
38+
}
39+
40+
[Theory]
41+
[InlineData(typeof(OpenApiDocument), typeof(DocumentVisitor))]
42+
[InlineData(typeof(OpenApiInfo), typeof(InfoVisitor))]
43+
[InlineData(typeof(OpenApiXml), typeof(XmlVisitor))]
44+
[InlineData(typeof(OpenApiComponents), typeof(ComponentsVisitor))]
45+
public void GetVisitorReturnsTheCorrectVisitor(Type elementType, Type visitorType)
46+
{
47+
// Arrange & Act
48+
var visitor = OpenApiVisitorSet.GetVisitor(elementType);
49+
50+
// Assert
51+
Assert.NotNull(visitor);
52+
Assert.Same(visitorType, visitor.GetType());
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)