Fauna v10 .NET/C# Driver 1.0.1
 
Loading...
Searching...
No Matches
Client.cs
Go to the documentation of this file.
1using System.Globalization;
2using Fauna.Core;
4using Fauna.Linq;
5using Fauna.Mapping;
7using Fauna.Types;
8using Stream = System.IO.Stream;
9
10namespace Fauna;
11
15public class Client : BaseClient, IDisposable
16{
17 private const string QueryUriPath = "/query/1";
18 private const string StreamUriPath = "/stream/1";
19 private const string FeedUriPath = "/feed/1";
20
21 private readonly Configuration _config;
22 private readonly IConnection _connection;
23
24 private readonly MappingContext _defaultCtx = new();
25 private readonly Dictionary<Type, DataContext> _dbCtxs = new();
26
27 private bool _disposed;
28
29 internal override MappingContext MappingCtx { get => _defaultCtx; }
30
35
39 public long LastSeenTxn { get; private set; }
40
45 public Client() : this(new Configuration()) { }
46
51 public Client(string secret) :
52 this(new Configuration(secret))
53 {
54 }
55
60 public Client(Configuration config)
61 {
62 config.Validate();
63 _config = config;
65 _connection = new Connection(config);
66 }
67
73 public DB DataContext<DB>() where DB : DataContext
74 {
75 var dbCtxType = typeof(DB);
76 DataContext? ctx;
77 lock (_dbCtxs)
78 {
79 if (!_dbCtxs.TryGetValue(dbCtxType, out ctx))
80 {
81 var builder = new DataContextBuilder<DB>();
82 ctx = builder.Build(this);
83 _dbCtxs[dbCtxType] = ctx;
84 }
85 }
86
87 return (DB)ctx;
88 }
89
93 public void Dispose()
94 {
95 Dispose(true);
96 GC.SuppressFinalize(this);
97 }
98
104 ~Client()
105 {
106 Dispose(false);
107 }
108
109 internal override async Task<QuerySuccess<T>> QueryAsyncInternal<T>(
110 Query query,
111 ISerializer<T> serializer,
112 MappingContext ctx,
113 QueryOptions? queryOptions,
114 CancellationToken cancel)
115 {
116 if (query == null)
117 {
118 throw new ArgumentNullException(nameof(query));
119 }
120
121 var finalOptions = QueryOptions.GetFinalQueryOptions(_config.DefaultQueryOptions, queryOptions);
122 var headers = GetRequestHeaders(finalOptions);
123
124 using var stream = new MemoryStream();
125 Serialize(stream, query, ctx);
126
127 using var httpResponse = await _connection.DoPostAsync(
128 QueryUriPath,
129 stream,
130 headers,
131 GetRequestTimeoutWithBuffer(finalOptions.QueryTimeout),
132 cancel);
133 var body = await httpResponse.Content.ReadAsStringAsync(cancel);
134 var res = QueryResponse.GetFromResponseBody<T>(ctx, serializer, httpResponse.StatusCode, body);
135 switch (res)
136 {
137 case QuerySuccess<T> success:
139 StatsCollector?.Add(res.Stats);
140 return success;
141 case QueryFailure failure:
142 StatsCollector?.Add(res.Stats);
143 throw ExceptionHandler.FromQueryFailure(ctx, failure);
144 default:
145 throw ExceptionHandler.FromRawResponse(body, httpResponse);
146 }
147 }
148
149 internal override async IAsyncEnumerator<Event<T>> SubscribeStreamInternal<T>(
150 Types.EventSource eventSource,
151 MappingContext ctx,
152 CancellationToken cancel = default)
153 {
154 var finalOptions = QueryOptions.GetFinalQueryOptions(_config.DefaultQueryOptions, null);
155 var headers = GetRequestHeaders(finalOptions);
156
157 await foreach (var evt in _connection.OpenStream<T>(
158 StreamUriPath,
159 eventSource,
160 headers,
161 ctx,
162 cancel))
163 {
164 LastSeenTxn = evt.TxnTime;
165 eventSource.Options.Cursor = evt.Cursor;
166
167 StatsCollector?.Add(evt.Stats);
168 yield return evt;
169 }
170 }
171 internal override async IAsyncEnumerator<FeedPage<T>> SubscribeFeedInternal<T>(
172 Types.EventSource eventSource,
173 MappingContext ctx,
174 CancellationToken cancel = default)
175 {
176 cancel.ThrowIfCancellationRequested();
177
178 var finalOptions = _config.DefaultQueryOptions;
179 var headers = GetRequestHeaders(finalOptions);
180
181 while (!cancel.IsCancellationRequested)
182 {
183 var feedData = new MemoryStream();
184 eventSource.Serialize(feedData);
185
186 using var httpResponse = await _connection.DoPostAsync(
187 FeedUriPath,
188 feedData,
189 headers,
190 GetRequestTimeoutWithBuffer(finalOptions.QueryTimeout),
191 cancel);
192 string body = await httpResponse.Content.ReadAsStringAsync(cancel);
193
194 var res = FeedPage<T>.From(body, ctx);
195 eventSource.Options.Cursor = res.Cursor;
196
197 StatsCollector?.Add(res.Stats);
198 yield return res;
199 if (!res.HasNext)
200 {
201 break;
202 }
203 }
204 }
205
206 private void Serialize(Stream stream, Query query, MappingContext ctx)
207 {
208 using var writer = new Utf8FaunaWriter(stream);
209 writer.WriteStartObject();
210 writer.WriteFieldName("query");
211 query.Serialize(ctx, writer);
212 writer.WriteEndObject();
213 writer.Flush();
214 }
215
216 private Dictionary<string, string> GetRequestHeaders(QueryOptions queryOptions)
217 {
218 var headers = new Dictionary<string, string>
219 {
220 { Headers.Authorization, $"Bearer {_config.Secret}"},
221 { Headers.Format, "tagged" },
222 { Headers.Driver, "C#" }
223 };
224
225 if (LastSeenTxn > long.MinValue)
226 {
227 headers.Add(Headers.LastTxnTs, LastSeenTxn.ToString());
228 }
229
230 if (queryOptions.QueryTimeout != TimeSpan.Zero)
231 {
232 headers.Add(
233 Headers.QueryTimeoutMs,
234 queryOptions.QueryTimeout.TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
235 }
236
237 if (queryOptions.QueryTags != null)
238 {
239 headers.Add(Headers.QueryTags, EncodeQueryTags(queryOptions.QueryTags));
240 }
241
242 if (!string.IsNullOrEmpty(queryOptions.TraceParent))
243 {
244 headers.Add(Headers.TraceParent, queryOptions.TraceParent);
245 }
246
247 if (queryOptions.Linearized != null)
248 {
249 headers.Add(Headers.Linearized, queryOptions.Linearized.ToString()!);
250 }
251
252 if (queryOptions.TypeCheck != null)
253 {
254 headers.Add(Headers.TypeCheck, queryOptions.TypeCheck.ToString()!);
255 }
256
257 return headers;
258 }
259
260 private TimeSpan GetRequestTimeoutWithBuffer(TimeSpan queryTimeout)
261 {
262 return queryTimeout.Add(_config.ClientBufferTimeout);
263 }
264
265 private static string EncodeQueryTags(Dictionary<string, string> tags)
266 {
267 return string.Join(",", tags.Select(entry => entry.Key + "=" + entry.Value));
268 }
269
270 private void Dispose(bool disposing)
271 {
272 if (_disposed) return;
273
274 if (disposing)
275 {
276 _connection.Dispose();
277 GC.SuppressFinalize(this);
278 }
279 _disposed = true;
280 }
281}
System.IO.Stream Stream
Definition Client.cs:8
The base class for Client and DataContext.
Definition IClient.cs:370
Represents a client for interacting with a Fauna.
Definition Client.cs:16
Client()
Initializes a new instance of a Client with the default configuration. Assumes the environment variab...
Definition Client.cs:45
readonly? IStatsCollector StatsCollector
Provides collection and aggregation of query statistics. Can be set to null in the Configuration.
Definition Client.cs:34
Client(Configuration config)
Initializes a new instance of the Client with a custom Configuration.
Definition Client.cs:60
Client(string secret)
Initializes a new instance of a Client with a secret.
Definition Client.cs:51
long LastSeenTxn
Gets the timestamp of the last transaction seen by this client.
Definition Client.cs:39
DB DataContext< DB >()
Create and return a new database context which uses the Client instance.
Definition Client.cs:73
void Dispose()
Disposes the resources used by the Client class.
Definition Client.cs:93
Configuration is a class used to configure a Fauna Client. It encapsulates various settings such as t...
IStatsCollector? StatsCollector
StatsCollector for the client.
Represents the response from Fauna Event Feed requests.
Definition FeedPage.cs:14
Represents a failed query response.
Represents the options for customizing Fauna queries.
bool? TypeCheck
Gets or sets a value indicating whether type checking of the query is enabled or disabled before eval...
Dictionary< string, string >? QueryTags
Gets or sets a string-encoded set of caller-defined tags for identifying the request in logs and resp...
TimeSpan QueryTimeout
Gets or sets the query timeout. It defines how long the client waits for a query to complete....
string? TraceParent
Gets or sets the trace parent identifier for distributed tracing systems.
bool? Linearized
Gets or sets a value indicating whether the query runs as strictly serialized, affecting read-only tr...
Represents the response from a query executed.
long LastSeenTxn
Gets the last transaction seen by this query.
Represents a successful query response.
The default implementation of IStatsCollector.
void Add(QueryStats stats)
Add the QueryStats to the current counts.
An abstract class representing a DataContext. This is a special type of Fauna client that can be used...
A class representing the mapping context to be used during serialization and deserialization.
Represents the abstract base class for constructing FQL queries.
Definition Query.cs:11
void Serialize(MappingContext ctx, Utf8FaunaWriter writer)
Serializes the query into the provided stream.
Provides functionality for writing data in a streaming manner to a buffer or a stream.
An interface used by a client instance for aggregating stats across all queries.
A generic interface that defines serialize and deserialize behavior for a specific type,...