Skip to content

Commit 79b009c

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

File tree

1 file changed

+89
-79
lines changed

1 file changed

+89
-79
lines changed

Fritz.InstantAPIs/WebApplicationExtensions.cs

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

92102
}

0 commit comments

Comments
 (0)