Skip to content

Instantly share code, notes, and snippets.

@JulianR
Created March 8, 2012 02:01
Show Gist options
  • Save JulianR/1998017 to your computer and use it in GitHub Desktop.
Save JulianR/1998017 to your computer and use it in GitHub Desktop.
Generic compound service
public class BatchClient
{
private readonly CeyenneClient _client;
private List<object> _batch;
private CompoundResponse _response;
public BatchClient(CeyenneClient client)
{
_client = client;
_batch = new List<object>();
}
public void AddMessage(object message)
{
_batch.Add(message);
}
public void SendBatch()
{
var compound = new Compound
{
Values = _batch.ToDictionary(k => k.GetType().Name, v => TypeSerializer.SerializeToString(v))
};
_response = _client.Send(compound);
}
public T Get<T>()
{
if (_response == null)
{
throw new InvalidOperationException("Batch has not been sent yet");
}
string value;
if (!_response.Result.TryGetValue(typeof(T).Name, out value))
{
return default(T);
}
var result = TypeSerializer.DeserializeFromString<T>(value);
return result;
}
}
var client = new CeyenneClient("http://localhost/Services/servicestack");
var batch = client.CreateBatch();
batch.AddMessage(new GetUser
{
ID = 1
});
batch.AddMessage(new GetStation
{
ID = 1
});
batch.SendBatch();
var result = batch.Get<GetUserResponse>();
var result1 = batch.Get<GetStationResponse>();
[Serializable]
public class Compound : RequestMessage, IRequestRespondsAs<CompoundResponse>
{
public bool IsJson { get; set; }
public Dictionary<string, string> Values { get; set; }
}
[Serializable]
public class CompoundResponse : ResponseMessage
{
public Dictionary<string, string> Result { get; set; }
}
public class CompoundService : IService<Compound>, IRequiresRequestContext
{
private static Dictionary<string, ServiceTuple> _serviceCache;
private class ServiceTuple
{
public Type ServiceType { get; set; }
public Type MessageType { get; set; }
public Func<CompoundService, object, object> ServiceRunner { get; set; }
}
private static void CreateServiceCache()
{
var services = (from t in EndpointHost.ServiceManager.ServiceController.ServiceTypes
where t.IsClass
&& !t.IsAbstract
let serviceInterface = t.GetInterface("IService`1")
where serviceInterface != null
let messageType = serviceInterface.GetGenericArguments().Single()
select new ServiceTuple
{
ServiceType = t,
MessageType = messageType
});
_serviceCache = services.ToDictionary(k => k.MessageType.Name);
}
private static readonly MethodInfo _resolveMethod = typeof(CompoundService)
.GetMethod("ResolveService", BindingFlags.NonPublic | BindingFlags.Instance);
private static Func<CompoundService, object, object> CreateServiceRunner(Type service, Type message)
{
var getMethod = _resolveMethod.MakeGenericMethod(service);
var serviceParam = Expression.Parameter(typeof(CompoundService), "service");
var callGet = Expression.Call(serviceParam, getMethod);
var messageParam = Expression.Parameter(typeof(object), "message");
var executeMethod = typeof(IService<>).MakeGenericType(message)
.GetMethod("Execute");
var responseParam = Expression.Variable(typeof(object), "response");
var castToMessage = Expression.Convert(messageParam, message);
var callExecute = Expression.Call(callGet, executeMethod, castToMessage);
var assignToResponse = Expression.Assign(responseParam, callExecute);
var lambda = Expression.Lambda<Func<CompoundService, object, object>>(callExecute, serviceParam, messageParam);
return lambda.Compile();
}
static CompoundService()
{
CreateServiceCache();
}
public object Execute(Compound request)
{
var results = new Dictionary<string, string>();
foreach (var kv in request.Values)
{
ServiceTuple serviceTypes;
if (!_serviceCache.TryGetValue(kv.Key, out serviceTypes))
{
continue;
}
if (serviceTypes.ServiceRunner == null)
{
serviceTypes.ServiceRunner = CreateServiceRunner(serviceTypes.ServiceType, serviceTypes.MessageType);
}
object message;
if (request.IsJson)
{
message = JsonSerializer.DeserializeFromString(kv.Value, serviceTypes.MessageType);
}
else
{
message = TypeSerializer.DeserializeFromString(kv.Value, serviceTypes.MessageType);
}
var response = serviceTypes.ServiceRunner(this, message);
string responseText;
if (request.IsJson)
{
responseText = JsonSerializer.SerializeToString(response);
}
else
{
responseText = TypeSerializer.SerializeToString(response);
}
results.Add(response.GetType().Name, responseText);
}
return new CompoundResponse
{
Result = results
};
}
protected T ResolveService<T>()
{
var service = EndpointHost.AppHost.TryResolve<T>();
var requiresContext = service as IRequiresRequestContext;
if (requiresContext != null)
{
requiresContext.RequestContext = this.RequestContext;
}
return service;
}
public IRequestContext RequestContext { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment