-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathEfGraphQLService_NavigationConnection.cs
More file actions
123 lines (108 loc) · 4.36 KB
/
EfGraphQLService_NavigationConnection.cs
File metadata and controls
123 lines (108 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
namespace GraphQL.EntityFramework;
partial class EfGraphQLService<TDbContext>
where TDbContext : DbContext
{
static MethodInfo addEnumerableConnection = typeof(EfGraphQLService<TDbContext>)
.GetMethod("AddEnumerableConnection", BindingFlags.Instance | BindingFlags.NonPublic)!;
public ConnectionBuilder<TSource> AddNavigationConnectionField<TSource, TReturn>(
ComplexGraphType<TSource> graph,
string name,
Func<ResolveEfFieldContext<TDbContext, TSource>, IEnumerable<TReturn>>? resolve = null,
Type? itemGraphType = null,
IEnumerable<string>? includeNames = null,
bool omitQueryArguments = false)
where TReturn : class
{
Guard.AgainstWhiteSpace(nameof(name), name);
itemGraphType ??= GraphTypeFinder.FindGraphType<TReturn>();
var addConnectionT = addEnumerableConnection.MakeGenericMethod(typeof(TSource), itemGraphType, typeof(TReturn));
try
{
var arguments = new object?[]
{
graph,
name,
resolve,
includeNames,
omitQueryArguments
};
return (ConnectionBuilder<TSource>) addConnectionT.Invoke(this, arguments)!;
}
catch (Exception exception)
{
throw new(
$"""
Failed to execute navigation connection for field `{name}`
ItemGraphType: {itemGraphType.FullName}
TSource: {typeof(TSource).FullName}
TReturn: {typeof(TReturn).FullName}
""",
exception);
}
}
ConnectionBuilder<TSource> AddEnumerableConnection<TSource, TGraph, TReturn>(
ComplexGraphType<TSource> graph,
string name,
Func<ResolveEfFieldContext<TDbContext, TSource>, IEnumerable<TReturn>>? resolve,
IEnumerable<string>? includeNames,
bool omitQueryArguments)
where TGraph : IGraphType
where TReturn : class
{
var builder = ConnectionBuilderEx<TSource>.Build<TGraph>(name);
IncludeAppender.SetIncludeMetadata(builder.FieldType, name, includeNames);
var hasId = keyNames.ContainsKey(typeof(TReturn));
if (resolve is not null)
{
builder.ResolveAsync(async context =>
{
var efFieldContext = BuildContext(context);
IEnumerable<TReturn> enumerable;
try
{
enumerable = resolve(efFieldContext);
}
catch (TaskCanceledException)
{
throw;
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception exception)
{
throw new(
$"""
Failed to execute query for field `{name}`
TGraph: {typeof(TGraph).FullName}
TSource: {typeof(TSource).FullName}
TReturn: {typeof(TReturn).FullName}
""",
exception);
}
if (enumerable is IQueryable)
{
throw new("This API expects the resolver to return a IEnumerable, not an IQueryable. Instead use AddQueryConnectionField.");
}
enumerable = enumerable.ApplyGraphQlArguments(hasId, context, omitQueryArguments);
if (efFieldContext.Filters != null)
{
enumerable = await efFieldContext.Filters.ApplyFilter(enumerable, context.UserContext, efFieldContext.DbContext, context.User);
}
var page = enumerable.ToList();
return ConnectionConverter.ApplyConnectionContext(
page,
context.First,
context.After,
context.Last,
context.Before);
});
}
//TODO: works around https://github.com/graphql-dotnet/graphql-dotnet/pull/2581/
builder.FieldType.Type = typeof(NonNullGraphType<ConnectionType<TGraph, EdgeType<TGraph>>>);
var field = graph.AddField(builder.FieldType);
field.AddWhereArgument(hasId);
return builder;
}
}