-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathPersonQuery.cs
More file actions
195 lines (172 loc) · 7.97 KB
/
PersonQuery.cs
File metadata and controls
195 lines (172 loc) · 7.97 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dapper.GraphQL.Test.EntityMappers;
using Dapper.GraphQL.Test.Models;
using GraphQL.Builders;
using GraphQL.Types;
using GraphQL.Types.Relay.DataObjects;
using Microsoft.Extensions.DependencyInjection;
using Dapper.GraphQL.Test.Repositories;
namespace Dapper.GraphQL.Test.GraphQL
{
public class PersonQuery :
ObjectGraphType
{
private const int MaxPageSize = 10;
private readonly IPersonRepository _personRepository;
public PersonQuery(
IQueryBuilder<Person> personQueryBuilder,
IServiceProvider serviceProvider,
IPersonRepository personRepository)
{
_personRepository = personRepository;
Field<ListGraphType<PersonType>>(
"people",
description: "A list of people.",
resolve: context =>
{
var alias = "person";
var query = SqlBuilder
.From<Person>(alias)
.OrderBy($"{alias}.Id");
query = personQueryBuilder.Build(query, context.FieldAst, alias);
// Create a mapper that understands how to uniquely identify the 'Person' class,
// and will deduplicate people as they pass through it
var personMapper = new PersonEntityMapper();
using (var connection = serviceProvider.GetRequiredService<IDbConnection>())
{
var results = query
.Execute(connection, context.FieldAst, personMapper)
.Distinct();
return results;
}
}
);
FieldAsync<ListGraphType<PersonType>>(
"peopleAsync",
description: "A list of people fetched asynchronously.",
resolve: async context =>
{
var alias = "person";
var query = SqlBuilder
.From($"Person {alias}")
.OrderBy($"{alias}.Id");
query = personQueryBuilder.Build(query, context.FieldAst, alias);
// Create a mapper that understands how to uniquely identify the 'Person' class,
// and will deduplicate people as they pass through it
var personMapper = new PersonEntityMapper();
using (var connection = serviceProvider.GetRequiredService<IDbConnection>())
{
connection.Open();
var results = await query.ExecuteAsync(connection, context.FieldAst, personMapper);
return results.Distinct();
}
}
);
Field<PersonType>(
"person",
description: "Gets a person by ID.",
arguments: new QueryArguments(
new QueryArgument<IntGraphType> { Name = "id", Description = "The ID of the person." }
),
resolve: context =>
{
var id = context.Arguments["id"];
var alias = "person";
var query = SqlBuilder
.From($"Person {alias}")
.Where($"{alias}.Id = @id", new { id })
// Even though we're only getting a single person, the process of deduplication
// may return several entities, so we sort by ID here for consistency
// with test results.
.OrderBy($"{alias}.Id");
query = personQueryBuilder.Build(query, context.FieldAst, alias);
// Create a mapper that understands how to uniquely identify the 'Person' class,
// and will deduplicate people as they pass through it
var personMapper = new PersonEntityMapper();
using (var connection = serviceProvider.GetRequiredService<IDbConnection>())
{
var results = query
.Execute(connection, context.FieldAst, personMapper)
.Distinct();
return results.FirstOrDefault();
}
}
);
Connection<PersonType>()
.Name("personConnection")
.Description("Gets pages of Person objects.")
// Enable the last and before arguments to do paging in reverse.
.Bidirectional()
// Set the maximum size of a page, use .ReturnAll() to set no maximum size.
.PageSize(MaxPageSize)
.ResolveAsync(context => ResolveConnection(context, personQueryBuilder));
}
private async Task<object> ResolveConnection(ResolveConnectionContext<object> context, IQueryBuilder<Person> personQueryBuilder)
{
_personRepository.Context = context;
var first = context.First;
var afterCursor = Cursor.FromCursor<DateTime?>(context.After);
var last = context.Last;
var beforeCursor = Cursor.FromCursor<DateTime?>(context.Before);
var cancellationToken = context.CancellationToken;
var getPersonTask = GetPeople(first, afterCursor, last, beforeCursor, cancellationToken);
var getHasNextPageTask = GetHasNextPage(first, afterCursor, cancellationToken);
var getHasPreviousPageTask = GetHasPreviousPage(last, beforeCursor, cancellationToken);
var totalCountTask = _personRepository.GetTotalCount(cancellationToken);
await Task.WhenAll(getPersonTask, getHasNextPageTask, getHasPreviousPageTask, totalCountTask);
var people = getPersonTask.Result;
var hasNextPage = getHasNextPageTask.Result;
var hasPreviousPage = getHasPreviousPageTask.Result;
var totalCount = totalCountTask.Result;
var (firstCursor, lastCursor) = Cursor.GetFirstAndLastCursor(people, x => x.CreateDate);
return new Connection<Person>()
{
Edges = people
.Select(x =>
new Edge<Person>()
{
Cursor = Cursor.ToCursor(x.CreateDate),
Node = x
})
.ToList(),
PageInfo = new PageInfo()
{
HasNextPage = hasNextPage,
HasPreviousPage = hasPreviousPage,
StartCursor = firstCursor,
EndCursor = lastCursor,
},
TotalCount = totalCount,
};
}
private async Task<bool> GetHasNextPage(
int? first,
DateTime? afterCursor,
CancellationToken cancellationToken)
{
return first.HasValue ? await _personRepository.GetHasNextPage(first, afterCursor, cancellationToken) : false;
}
private async Task<bool> GetHasPreviousPage(
int? last,
DateTime? beforeCursor,
CancellationToken cancellationToken)
{
return last.HasValue ? await _personRepository.GetHasPreviousPage(last, beforeCursor, cancellationToken) : false;
}
private Task<List<Person>> GetPeople(
int? first,
DateTime? afterCursor,
int? last,
DateTime? beforeCursor,
CancellationToken cancellationToken)
{
return first.HasValue ? _personRepository.GetPeople(first, afterCursor, cancellationToken) :
_personRepository.GetPeopleReversed(last, beforeCursor, cancellationToken);
}
}
}