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)
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;
}
}
Instantiating the implementation could be as simple as:
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?
No comments:
Post a Comment