Skip to content

Commit abfc73c

Browse files
committed
Refactored reflection addition of InstantAPIs
1 parent f2e8970 commit abfc73c

File tree

1 file changed

+90
-79
lines changed

1 file changed

+90
-79
lines changed

Fritz.InstantAPIs/WebApplicationExtensions.cs

Lines changed: 90 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,84 +9,95 @@ namespace Microsoft.AspNetCore.Builder;
99
public static class WebApplicationExtensions
1010
{
1111

12-
private static InstantAPIsConfig Configuration { get; set; } = new();
13-
14-
public static IEndpointRouteBuilder MapInstantAPIs<D>(this IEndpointRouteBuilder app, Action<InstantAPIsConfigBuilder<D>> options = null) where D : DbContext
15-
{
16-
if (app is IApplicationBuilder applicationBuilder)
17-
{
18-
// Check if AddInstantAPIs was called by getting the service options and evaluate EnableSwagger property
19-
var serviceOptions = applicationBuilder.ApplicationServices.GetRequiredService<IOptions<InstantAPIsServiceOptions>>().Value;
20-
if (serviceOptions == null || serviceOptions.EnableSwagger == null)
21-
{
22-
throw new ArgumentException("Call builder.Services.AddInstantAPIs(options) before MapInstantAPIs.");
23-
}
24-
25-
var webApp = (WebApplication)app;
26-
if (serviceOptions.EnableSwagger == EnableSwagger.Always ||
27-
(serviceOptions.EnableSwagger == EnableSwagger.DevelopmentOnly && webApp.Environment.IsDevelopment()))
28-
{
29-
applicationBuilder.UseSwagger();
30-
applicationBuilder.UseSwaggerUI();
31-
}
32-
33-
var ctx = applicationBuilder.ApplicationServices.CreateScope().ServiceProvider.GetService(typeof(D)) as D;
34-
var builder = new InstantAPIsConfigBuilder<D>(ctx);
35-
if (options != null)
36-
{
37-
options(builder);
38-
Configuration = builder.Build();
39-
}
40-
}
41-
42-
// Get the tables on the DbContext
43-
var dbTables = GetDbTablesForContext<D>();
44-
45-
var requestedTables = !Configuration.Tables.Any() ?
46-
dbTables :
47-
Configuration.Tables.Where(t => dbTables.Any(db => db.Name.Equals(t.Name, StringComparison.OrdinalIgnoreCase))).ToArray();
48-
49-
var allMethods = typeof(MapApiExtensions).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name.StartsWith("Map")).ToArray();
50-
var initialize = typeof(MapApiExtensions).GetMethod("Initialize", BindingFlags.NonPublic | BindingFlags.Static);
51-
foreach (var table in requestedTables)
52-
{
53-
54-
// The default URL for an InstantAPI is /api/TABLENAME
55-
var url = $"/api/{table.Name}";
56-
57-
initialize.MakeGenericMethod(typeof(D), table.InstanceType).Invoke(null, null);
58-
59-
// The remaining private static methods in this class build out the Mapped API methods..
60-
// let's use some reflection to get them
61-
foreach (var method in allMethods)
62-
{
63-
64-
var sigAttr = method.CustomAttributes.First(x => x.AttributeType == typeof(ApiMethodAttribute)).ConstructorArguments.First();
65-
var methodType = (ApiMethodsToGenerate)sigAttr.Value;
66-
if ((table.ApiMethodsToGenerate & methodType) != methodType) continue;
67-
68-
var genericMethod = method.MakeGenericMethod(typeof(D), table.InstanceType);
69-
genericMethod.Invoke(null, new object[] { app, url });
70-
}
71-
72-
}
73-
74-
return app;
75-
}
76-
77-
internal static IEnumerable<TypeTable> GetDbTablesForContext<D>() where D : DbContext
78-
{
79-
return typeof(D).GetProperties(BindingFlags.Instance | BindingFlags.Public)
80-
.Where(x => x.PropertyType.FullName.StartsWith("Microsoft.EntityFrameworkCore.DbSet"))
81-
.Select(x => new TypeTable { Name = x.Name, InstanceType = x.PropertyType.GenericTypeArguments.First() })
82-
.ToArray();
83-
}
84-
85-
internal class TypeTable
86-
{
87-
public string Name { get; set; }
88-
public Type InstanceType { get; set; }
89-
public ApiMethodsToGenerate ApiMethodsToGenerate { get; set; } = ApiMethodsToGenerate.All;
90-
}
12+
private static InstantAPIsConfig Configuration { get; set; } = new();
13+
14+
public static IEndpointRouteBuilder MapInstantAPIs<D>(this IEndpointRouteBuilder app, Action<InstantAPIsConfigBuilder<D>> options = null) where D : DbContext
15+
{
16+
if (app is IApplicationBuilder applicationBuilder)
17+
{
18+
AddOpenAPIConfiguration(app, options, applicationBuilder);
19+
}
20+
21+
// Get the tables on the DbContext
22+
var dbTables = GetDbTablesForContext<D>();
23+
24+
var requestedTables = !Configuration.Tables.Any() ?
25+
dbTables :
26+
Configuration.Tables.Where(t => dbTables.Any(db => db.Name.Equals(t.Name, StringComparison.OrdinalIgnoreCase))).ToArray();
27+
28+
MapInstantAPIsUsingReflection<D>(app, requestedTables);
29+
30+
return app;
31+
}
32+
33+
private static void MapInstantAPIsUsingReflection<D>(IEndpointRouteBuilder app, IEnumerable<TypeTable> requestedTables) where D : DbContext
34+
{
35+
36+
var allMethods = typeof(MapApiExtensions).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name.StartsWith("Map")).ToArray();
37+
var initialize = typeof(MapApiExtensions).GetMethod("Initialize", BindingFlags.NonPublic | BindingFlags.Static);
38+
foreach (var table in requestedTables)
39+
{
40+
41+
// The default URL for an InstantAPI is /api/TABLENAME
42+
var url = $"/api/{table.Name}";
43+
44+
initialize.MakeGenericMethod(typeof(D), table.InstanceType).Invoke(null, null);
45+
46+
// The remaining private static methods in this class build out the Mapped API methods..
47+
// let's use some reflection to get them
48+
foreach (var method in allMethods)
49+
{
50+
51+
var sigAttr = method.CustomAttributes.First(x => x.AttributeType == typeof(ApiMethodAttribute)).ConstructorArguments.First();
52+
var methodType = (ApiMethodsToGenerate)sigAttr.Value;
53+
if ((table.ApiMethodsToGenerate & methodType) != methodType) continue;
54+
55+
var genericMethod = method.MakeGenericMethod(typeof(D), table.InstanceType);
56+
genericMethod.Invoke(null, new object[] { app, url });
57+
}
58+
59+
}
60+
}
61+
62+
private static void AddOpenAPIConfiguration<D>(IEndpointRouteBuilder app, Action<InstantAPIsConfigBuilder<D>> options, IApplicationBuilder applicationBuilder) where D : DbContext
63+
{
64+
// Check if AddInstantAPIs was called by getting the service options and evaluate EnableSwagger property
65+
var serviceOptions = applicationBuilder.ApplicationServices.GetRequiredService<IOptions<InstantAPIsServiceOptions>>().Value;
66+
if (serviceOptions == null || serviceOptions.EnableSwagger == null)
67+
{
68+
throw new ArgumentException("Call builder.Services.AddInstantAPIs(options) before MapInstantAPIs.");
69+
}
70+
71+
var webApp = (WebApplication)app;
72+
if (serviceOptions.EnableSwagger == EnableSwagger.Always ||
73+
(serviceOptions.EnableSwagger == EnableSwagger.DevelopmentOnly && webApp.Environment.IsDevelopment()))
74+
{
75+
applicationBuilder.UseSwagger();
76+
applicationBuilder.UseSwaggerUI();
77+
}
78+
79+
var ctx = applicationBuilder.ApplicationServices.CreateScope().ServiceProvider.GetService(typeof(D)) as D;
80+
var builder = new InstantAPIsConfigBuilder<D>(ctx);
81+
if (options != null)
82+
{
83+
options(builder);
84+
Configuration = builder.Build();
85+
}
86+
}
87+
88+
internal static IEnumerable<TypeTable> GetDbTablesForContext<D>() where D : DbContext
89+
{
90+
return typeof(D).GetProperties(BindingFlags.Instance | BindingFlags.Public)
91+
.Where(x => x.PropertyType.FullName.StartsWith("Microsoft.EntityFrameworkCore.DbSet"))
92+
.Select(x => new TypeTable { Name = x.Name, InstanceType = x.PropertyType.GenericTypeArguments.First() })
93+
.ToArray();
94+
}
95+
96+
internal class TypeTable
97+
{
98+
public string Name { get; set; }
99+
public Type InstanceType { get; set; }
100+
public ApiMethodsToGenerate ApiMethodsToGenerate { get; set; } = ApiMethodsToGenerate.All;
101+
}
91102

92103
}

0 commit comments

Comments
 (0)