Saturday, November 22, 2014
Tuesday, March 18, 2014
Asynchronous Repository Pattern
I've been thinking a bit about network and other I/O latency problems and the traditional repository pattern. Started toying with the idea that a repository facade could explicitly expose a "promise-like" interface like the following.
public interface IRepository<T, in U> where T: class, new() where U: struct { Task<T> GetById(U id); Task<bool> Save(T entity); Task<bool> Remove(T entity); Task<IEnumerable<T>> GetAll(); }
Where T is the DTO and U is the underlying identifier type (int, Guid etc.)
This is all essentially client-side whereas the client is communicating with a resource (and perhaps resource is a better name than repository). So for instance, if I was communicating with a ReSTful service - my resource (or implementation of this particular pattern) could look something like the following.(This is currently implemented using RestSharp client using the ServiceStack.Text serializers)
Instantiating the implementation could be as simple as:public class RestfulClient<T, U> : RestClient, IRepository<T, U> where T: class, new() where U: struct { private readonly string _resource; private readonly ServiceStackJsonDeserializer deserializer; private readonly ServiceStackJsonSerializer serializer; private const string requestString = "application/json"; private const DataFormat requestFormat = DataFormat.Json; public RestfulClient(string baseUrl, string resource) : base(baseUrl) { this._resource = resource; this.deserializer = new ServiceStackJsonDeserializer(); this.serializer = new ServiceStackJsonSerializer(); ServiceStack.Text.JsConfig.EmitCamelCaseNames = true; this.AddHandler(requestString, new ServiceStackJsonDeserializer()); } private string UrlWithId(U id) { return string.Concat(this._resource, "/", id); } private RestRequest FormatRequest(string resource, Method method, T entity) { var req = new RestRequest(resource, method); if (entity != null) { req.RequestFormat = requestFormat; req.JsonSerializer = this.serializer; req.AddParameter(requestString, this.serializer.Serialize(entity), ParameterType.RequestBody); }; return req; } public Task<T> GetById(U id) { var t = Task.Factory.StartNew(() => this.deserializer.Deserialize<T>(this.ExecuteGetTaskAsync(FormatRequest(UrlWithId(id), Method.GET, null)).Result), CancellationToken.None); return t; } public Task<bool> Save(T entity) { var t = Task.Factory.StartNew(() => this.Execute(FormatRequest(this._resource, Method.POST, entity)).ResponseStatus == ResponseStatus.Completed, CancellationToken.None); return t; } public Task<bool> Remove(T entity) { var t = Task.Factory.StartNew(() => this.Execute(FormatRequest(this._resource, Method.DELETE, entity)).ResponseStatus == ResponseStatus.Completed, CancellationToken.None); return t; } public Task<IEnumerable<T>> GetAll() { var t = Task.Factory.StartNew(() => this.deserializer.Deserialize<IEnumerable<T>>(this.ExecuteTaskAsync(FormatRequest(this._resource, Method.GET, null)).Result), CancellationToken.None); return t; } }
var rc = new RestfulClient<ToDo, int>("http://localhost:53327", "api/v1/todos");Consuming looks like:
rc.GetById(id).ContinueWith((todo)=> Console.Write(todo.Result.Title));The nice thing is that the consuming client gets all the benefit of .ContinueWith (etc.) I'm not sure if I'm chasing dragons at this point - but I'm mulling it over...thoughts?
Subscribe to:
Posts (Atom)