At my current project we are using a dispatcher class to batch requests to our WCF layer based on the Request/Response Service Layer created by Davy Brion. Once you call the Get<TResponseType>() on the dispatcher the WCF layer starts working on the requests one by one and return them in one roundtrip.
Adding requests to the dispatcher is easy:
var saveDocumentRequest = new SaveDocumentRequest(); var getRemainingDocuments = new GetRemainingDocumentsRequest(); dispatcher.Add(saveDocumentRequest, getRemainingDocuments);
Where the signature of the Add method in Dispatcher is like;
public void Add(params Request[] requests)
Although this syntax is not bad, it feels like so much like code from last month: not very sexy. Wouldn't be more fun if we could spice it up a bit using generics and a bit of Func?
I'd love to get rid of instantiating a variable just for the sake of adding it to my dispatcher. After a bit of fiddling around with my unit tests I came up with the following solution.
What I want is to call the Add method in a generic way and set the parameters on my request object inline, using a Func. This is best illustrated with a test:
[Test] public void can_assign_value_using_a_func_in_a_generic_method_call() { Guid id = Guid.NewGuid(); var dispatcher = new Dispatcher(); dispatcher.Add<GetPersonsRequest>(s => s.ID = id); //disregard ugly cast; this is only for test purposes var request = (GetPersonsRequest)dispatcher.Requests.First(); request.ID.ShouldEqual(id); }
public class GetPersonsRequest:Request { public Guid ID { get; set; } } public class Dispatcher { public Dispatcher() { Requests = new List<Request>(); } public void Add<TRequestType>(params Func<TRequestType, object>[] funcs) where TRequestType : Request, new() { var request = new TRequestType(); foreach (var func in funcs) { func(request); } Requests.Add(request); } public IList<Request> Requests { get; set; } }
I can now set only property in my Request type, what if it has more properties that need to be set? Just introduce the infamous params to the method signature like so:
public void Add<TRequestType>(params Func<TRequestType, object>[] funcs) where TRequestType:Request, new() { var request = new TRequestType(); foreach (var func in funcs) { func(request); } Requests.Add(request); }
And again the test:
[Test] public void can_assign_two_values_using_a_func_in_a_generic_method_call() { var dispatcher = new Dispatcher(); Guid id = Guid.NewGuid(); dispatcher.Add<GetPersonsRequest>(s => s.ID = id, t=> t.GetAllDetails = true); //disregard ugly cast; this is only for test purposes var request = (GetPersonsRequest)dispatcher.Requests.First(); request.ID.ShouldEqual(id); request.GetAllDetails.ShouldBeTrue(); }
When we change the return type of the Add method to the dispatcher itself, we can then use a fluent interface to get a very friendly syntax for making calls:
//input parameters for first request var document = new object(); var userID = Guid.NewGuid(); //input parameters for second request var dossierID = Guid.NewGuid(); dispatcher .Add<SaveDocumentRequest>(s => s.Document = document, t => t.UserID = userID) .Add<GetZaakRequest>(s => s.DossierId = dossierID);
I really like the fact that I don't have to initialize a variable and still have access to all the properties in the request object..