Open Du-z opened 2 years ago
Right, so I've been mediating on the best practices to implement this. The current IDatabase
implementations are about as heavy as a HttpClient
, so we should use a pool for injecting a IDatabase
, (on that note, the interface should really be called ISurrealClient
to avoid confusion):
services.AddSingelton<ISurrealClientPool, SurrealClientPool>();
services.AddScoped<ISurrealClientHandle>(s => s.GetSingelton<ISurrealClientPool>().Rent()); // obtains a stale client from the pool, or create a new client
public sealed class SurrealClientHandle : ISurrealClient {
private readonly ISurrealClientPool _pool;
public ISurrealClient Client { get; } \\ gets the client instance, if not disposed
\\ Proxy for client implementation
void Dispose(); \\ returns the client to the pool, closes the connection, doesn't dispose the client
}
We expect the ISurrealClient
to be injected into a context object, which defined a strong typed API for a repository to interact with (Here we copy from the MongoDB driver/orm).
public sealed class SurveyContext : IDisposable {
private readonly ISurrealClient _survey;
private readonly ISurreadClient _user;
.ctor(ISurrealClient survey, ISurrealClient user, IOptions<SurveySettings> surveySettings, IOptions<UsersSettings> userSettingd) {
survey.SetConfig(Config.Create()[...]);
user.SetConfig(Config.Create()[...])
}
}
For this purpose we reduce the featureset of ISurrealClient
ONLY to allow for query, signin, authenticate. Signup, Change, Update and the other methods are just aliases for queries and do not require additional client sided functionality.
The interface will also return a IAsyncEnumerable<T>
using DeserializeAsyncEnumerable wrapping the networkstream instead of evaluating the entire response preemptively.
The reduction in functionality of the ISurrealClient
interface is groundwork for implementing an ORM, to interact with the database. Here I plan on copying the approach of the MongoDb driver for basic queries.
@Du-z Anything to add, before I start hacking it together?
In general I think stuff needs to be renamed to reduce ambiguity. Config
is probably the most obvious one that needs a better name.
I am always an advocate and go out of my way to make using a service as simple as possible. To that end i would say we should move the pooling down a layer of abstraction so the end user doesn't even have to know about the pooling if they don't want to know about it.
It's not clear to me how DatabaseRpc
and DatabaseRest
would be heavy if they get their respective WsClient
and HttpClient
injected along with their Config
.
DatabaseRest
would be able to reuse the exact same HttpClient
(even simultaneously). As long as we set the headers in the request using the config rather than the DefaultRequestHeaders
. I believe we are using the thread safe methods already https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-6.0#thread-safety.
DatabaseRpc
would probably need to use a pool for WsClient
.
Ultimately either way works for me though. If you can work in the ServiceLifetime
concept that would be great for those that can simply use a singleton IDatabase
Preamble
Currently Surreal.Net functions as a standard DB Driver where a single set of credentials is used for all connections to the database. However SurrealDB can do a lot more than your standard DB like generating and consuming access tokens. For this reason we must be able to isolate each users session.
Consideration
The Changes
Dependency Injection
Update the projects Dependency Injection to allow for the service and options lifetime to be configured when being defined.
EF core uses the ServiceLifetime Enum to define both the
contextLifetime
andoptionsLifetime
.By setting the
ServiceLifetime
to scoped each request (context of ASP.net) will have it's own instance of the SurrealDB driver.Some Considerations
The Rest and RPC drivers are currently setup to be consumed as it they are singletons and are not lightweight enough to be created and destroyed repeatedly.
Fixing this should be as simple as setting the Drivers to use DI for
HttpClient
andWsClient
(See typed clients).Config
will need to be injected also.Driver Changes
Don't use default headers for the
HttpClient
, set them on a per request basis based on what is in the supplied config. (Is there an equivalent forWsClient
?)Middleware
Create a simple bit of middleware to extract a JWT from an incoming request and insert it into the Surreal.Net configuration for the driver to use during that request.