BLOG

Aggregating WCF Service Results Asynchronously

30 September 2009 by Stuart Cam

I had a requirement recently to call multiple WCF endpoints and aggregate the results into a single list for further processing. The logical flow was similar to the following:

Logical flow

The WCF endpoints would take a reasonable length of time to return the results, so instead of executing the service calls in series I decided to create a new thread for each service call and return the aggregated list when all methods had completed.

The major pain point in any multithreaded code is managing access to shared resources between threads. If we create a temporary list for our results and insert new items we still need to manage access across multiple threads. Unfortunately, List<T> is not thread safe for modification, so we have to manage inserts through some kind of synchronisation mechanism - luckily we can use the lock(object){} syntax.

Since there are several places in the application where we'll need to do this type of aggregation I wrote some helper classes:

public abstract class MultiThreadedHelperWithBlock<TInput, TOutput>
{
    private readonly IList<Func<TInput, TOutput>> _functions = new List<Func<TInput, TOutput>>();
    private readonly object _mutex = new object();

    protected MultiThreadedHelperWithBlock<TInput, TOutput> ASyncMethod(Func<TInput, TOutput> function)
    {
        _functions.Add(function);
        return this;
    }

    protected void RunAsyncWithLockAndBlock(TInput input, Action<TOutput> action)
    {
        if(_functions.Count == 0)
            throw new Exception("Need to define at least one function to call");

        if(_functions.Count == 1)
        {
            action(_functions[0].Invoke(input));
            return;
        }

        var events = new ManualResetEvent[_functions.Count];
        for (var i = 0; i < _functions.Count; i++)
        {
            var index = i;
            var func = _functions[index];
            events[index] = new ManualResetEvent(false);
            var bw = new BackgroundWorker
            {
                WorkerReportsProgress = false,
                WorkerSupportsCancellation = false,
            };
            bw.DoWork += (sender, args) => args.Result = func.Invoke((TInput)args.Argument);
            bw.RunWorkerCompleted += (sender, args) =>
            {
                var result = (TOutput)args.Result;
                lock (_mutex)
                {
                    action(result);
                }
                events[index].Set();
            };
            bw.RunWorkerAsync(input);
        }
        WaitHandle.WaitAll(events);
    }
}

public class MultiThreadedListCombiner<TInput, TOutput> : MultiThreadedHelperWithBlock<TInput, IEnumerable<TOutput>>
{
    public new MultiThreadedListCombiner<TInput, TOutput> ASyncMethod(Func<TInput, IEnumerable<TOutput>> function)
    {
        return base.ASyncMethod(function) as MultiThreadedListCombiner<TInput, TOutput>;
    }

    public IEnumerable<TOutput> GetResults(TInput input)
    {
        var contents = new List<TOutput>();
        RunAsyncWithLockAndBlock(input, list =>
        {
            foreach (var item in list)
            {
                contents.Add(item);
            }
        });
        return contents;
    }
}

public class MultiThreadedServiceCall<TInterface, TInput, TOutput> : MultiThreadedHelperWithBlock<TInput, TOutput>
{
    private Func<TInterface, TInput, TOutput> _functionToCall;

    public MultiThreadedServiceCall<TInterface, TInput, TOutput> AttachService(TInterface service)
    {
        return ASyncMethod(request => _functionToCall.Invoke(service, request)) as MultiThreadedServiceCall<TInterface, TInput, TOutput>;
    }

    public MultiThreadedServiceCall<TInterface, TInput, TOutput> CallMethod(Func<TInterface, TInput, TOutput> functionToCall)
    {
        if (_functionToCall != null)
            throw new ArgumentException("CallMethod() has already been defined", "functionToCall");
        _functionToCall = functionToCall;
        return this;
    }

    public IEnumerable<TOutput> GetResults(TInput input)
    {
        if (_functionToCall == null) throw new NullReferenceException("Need to define CallMethod()");
        var contents = new List<TOutput>();
        RunAsyncWithLockAndBlock(input, contents.Add);
        return contents;
    }
}

OK, so the above code looks a little daunting - generics, closures and lambda expressions everywhere, but essentially all of the hard-work (such as thread spawning and synchronisation) is handled by MultiThreadedHelperWithBlock<>. The other two classes just inherit from this to perform different tasks.

The Enterprise Integration Patterns website has an interesting case study on the Loan Broker Ecosystem. Notice how the Loan broker calls multiple recipients and aggregates the results? I thought it'd be interesting to demonstrate how the above code can be used for this type of task.

Firstly we need a service interface contract, some data transfer objects and some service implementations:

// Service Contract
public interface ILoanService
{
    LoanResponse GetResponse(LoanRequest request);
}

// Data Transfer Objects
public struct LoanRequest
{
    public double Amount;
    public double Term;
}

public struct LoanResponse
{
    public string Lender;
    public double MoneyRepayment;
}

// Service Implementations
public class MoneyBagsLender : ILoanService
{
    public LoanResponse GetResponse(LoanRequest request)
    {
        Thread.Sleep(3500);
        Console.WriteLine("MoneyBags result returned");
        var repayment = (request.Amount / request.Term) * 1.05;
        return new LoanResponse { Lender = "MoneyBags", MoneyRepayment = repayment };
    }
}

public class RegularLender : ILoanService
{
    public LoanResponse GetResponse(LoanRequest request)
    {
        Thread.Sleep(5000);
        Console.WriteLine("RegularLender result returned");
        var repayment = (request.Amount / request.Term) * 1.2;
        return new LoanResponse { Lender = "Regular", MoneyRepayment = repayment };
    }
}

public class ShrewdLender : ILoanService
{
    public LoanResponse GetResponse(LoanRequest request)
    {
        Thread.Sleep(2000);
        Console.WriteLine("ShrewdLender result returned");
        var repayment = (request.Amount / request.Term) * 1.5;
        return new LoanResponse { Lender = "Shrewd", MoneyRepayment = repayment };
    }
}

The service implementations (ShrewdLender,RegularLender and MoneyBagsLender) will simulate "busy work" by just sleeping the threads for a certain amount of time.

Let's use the MultiThreadedServiceCall<> class to call these three services and return the list of LoanResponses:

// Create the Loan Request
var loanRequest = new LoanRequest { Amount = 10000, Term = 30 };

// Return an IEnumerable<LoanResponse>
var loanResponses = new MultiThreadedServiceCall<ILoanService, LoanRequest, LoanResponse>()
			    .CallMethod((service, request) => service.GetResponse(request))
			    .AttachService(new MoneyBagsLender())
			    .AttachService(new ShrewdLender())
			    .AttachService(new RegularLender())
			    .GetResults(loanRequest)
			    .OrderByAscending(response => response.MoneyRepayment);

// Display the results
foreach (var response in loanResponses)
{
	Console.WriteLine("Monthly repayment with lender '{0}' =  ${1}", response.Lender, response.MoneyRepayment);
}

This gives us the following output - the results take some time to be returned, but as soon as they are all completed the IEnumerable becomes available for further processing:

Console Output

Some Notes:

  • No error checking!
  • There may be some performance implications using the BackgroundWorker class to spawn threads - ideally we'd want to use a ThreadPool.
  • Each thread can support up to 64 WaitHandles (ManualResetEvents are built on these), although if you need to call more than 64 services you've probably got severe architectural issues.
  • .CallMethod((service, request) => service.GetResponse(request)) instructs the class to call the function GetResponse(LoanRequest request) on each service.
  • The input and output types are strongly typed by the generic type arguments.
  • .GetResults(loanRequest) returns an IEnumerable, so we are able to method chain LINQ queries.

The 3 type arguments to MultiThreadedServiceCall<> specify:

  1. The interface that each service implements.
  2. The input argument type for the specified method in .CallMethod(...).
  3. The output argument type for the specified method in .CallMethod(...).

Download the Program.cs (6.37 kb)

Tags: , , ,

Categories: .NET | C Sharp

My Current C# Development Stack

07 July 2009 by Stuart Cam

I have been fan boy of the ALT.NET movement for quite some time. I attended a few of the Sydney ALT.NET meetings when they first started and I have to say that they were remarkably more engaging than the Sydney Dot Net User Group (SDNUG). I'd like to extend a thank you to Richard Banks for starting the meetings, I really enjoyed them. If you are in Sydney I recommend you check them out and join the LinkedIn group.

I will be moving back to Bristol, UK on the 10th July 2009 and I am really pleased to hear that an ALT.NET Bristol group is starting up. Socialising, beers and geekery - a perfect combination! If you are interested then don't forget to join the ALT.NET Bristol LinkedIn Group as well.

I have tried to look outside of Redmond for .NET solutions to common problems, despite the fact that the framework is owned and developed by Microsoft. It would be fair to say that the .NET framework has attracted a lot of interest from some very smart software developers who have collaborated to create viable alternatives to Microsoft offerings. The best part is that they are usually free, well supported and open source.

  • Visual Source Safe? I'll take Subversion, thanks anyway.
  • MSTest? I'll take NUnit, thanks anyway.
  • Team System? I'll take CruiseControl, thanks anyway.
  • MSBuild? I'll take NAnt, thanks anyway.
  • ...the list goes on

My current (preferred) development stack looks something like:

If you are a .NET developer staring at the list above and thinking to yourself "what on earth is this guy talking about", I'd suggest that you check the fizzy liquid in your glass isn't Microsoft Kool Aid and visit some of the links!

Tags: , , ,

Categories: .NET | C Sharp | jQuery

I Read... There... I Said It!

03 July 2009 by Stuart Cam

I am sometimes asked "so how do you keep up-to-date with all that's happening in the industry?".

Well, I...

The problem is making time for these endeavours. Usually I will alternate between them, much like a buffet, occasionally gorging on a particular topic when I am in the right frame of mind. Obsessive? possibly... but I think it's important to have a varied diet to keep it exciting.

Online articles and coding websites are great for picking up the odd skill or two but they often lack the depth required for some topics.

I had attempted to reconcile my love of technical books and mitigate some of their downsides (size, weight, cost and errata) by purchasing a Sony PRS-500 and seeking out digital versions of titles. I bought the unit when it first launched a couple of years ago and since then it has seen relatively little action. It's been quite a disappointment. The main problem is screen size, or lack thereof. It's way too small to render diagrams and code snippets with clarity. If your primary goal is to read technical books then I'd suggest avoiding the unit altogether and consider a tablet PC instead. That, or take a look at the much larger 9.7" Kindle DX.

I have recently purchased a round of new books in dead-tree format, which I intend to read over the coming weeks...

Software Development

97 Things Every Software Architect Should Know - Various

97 Things Every Software Architect Should Know - Various

I originally found out about the book through Udi Dahan's blog. Udi has made a couple of article contributions and figured it would be worth a read since I respect his opinions on software development and admire his NServiceBus framework.

The book contains small snippets of non-technical advice from a variety of architects - experience which, in some cases, has been hard won.

The unedited contributions from each author are available for free.


SOA in Practice: The Art of Distributed System Design - Nicolai M. Josuttis

SOA in Practice: The Art of Distributed System Design - Nicolai M. Josuttis

What are the two rules of distributed computing?

1. Do not distribute, it's difficult
2. See #1 (asynchronously)

I know, I know, it's a terrible joke! I had been looking around for an authoritative book on SOA and distributed systems and this book came highly recommended for cutting through the hype. The accompanying website can be found here.


.NET Framework

Effective C#: 50 Specific Ways to Improve Your C# - Bill Wagner

Effective C#: 50 Specific Ways to Improve Your C# - Bill Wagner

I had originally found out about this book through my professional network on LinkedIn via Chris Fulstow. He had mentioned that he was reading the book so I figured it must be worth a look.

It's full of best practices specifically for C#, covering some of the more esoteric language 'features' and how to avoid digging yourself into a big hole with them.

Take a listen to the interview with Bill Wagner on .NET Rocks!


More Effective C#: 50 Specific Ways to Improve Your C# - Bill Wagner

More Effective C#: 50 Specific Ways to Improve Your C# - Bill Wagner

I figured I ought to buy the sequel as it contains information on how to get the best out of the newest C# features such as LINQ and Lambda Expressions.

I heard about this book through Chris Fulstow (again) and subsequently through another .NET Rocks! podcast with Bill Wagner.


ASP.NET MVC 1.0 Quickly - Maarten Balliauw

ASP.NET MVC 1.0 Quickly - Maarten Balliauw

I've been aware of the ASP.NET MVC framework for quite some time. Pretty much the day after Scott Guthrie announced a prototype at the ALT.NET conference. I was looking for a short book which would assume knowledge of ASP.NET and cut straight to the .NET MVC implementation.

Since v1.0 is fairly new there are numerous online tutorials for earlier versions and some great MVC blogs, but not much in the way of printed books. This looked to be the best of a small bunch.


Windows Presentation Foundation Unleashed - Adam Nathan

Windows Presentation Foundation Unleashed - Adam Nathan

I have a minor confession. I have zero WPF experience, which is a little embarrassing since it replaced GDI+ long, long ago.

Unlike many other technical books this one is printed on glossy paper in full colour and totally jam packed with pictures and diagrams. Perfect presentation for learning a presentation framework!

An ex-colleague, Jack Ukleja, recommended this book.


Other Technologies

The Definitive ANTLR Reference - Terrence Parr

The Definitive ANTLR Reference - Terrence Parr

I purchased a digital copy of this book some time ago and have nearly finished reading it... in the bath... on my PRS-500... wrapped rather optimistically in cling film. This time I figured I'd buy the paper version and risk the £16.88 it would cost me to replace if I dropped it!

Bath + electrical items != mix.


Compilers: Principles, Techniques and Tools - Various

Compilers: Principles, Techniques and Tools - Various

Probably the densest, in both physical and subject matter, of all the books I purchased. I had a quick flick through some of the material and it looks pretty heavy going and full of maths. I anticipate that this will take a long time to read and no doubt expose many other holes in my knowledge along the journey.

My primary motivation was to supplement the ANTLR book with other compiler topics.


Collective Intelligence in Action - Satnam Alag

Collective Intelligence in Action - Satnam Alag

A rather interesting book intended for a Java audience, but which contains mathematics and algorithms suitable for implementation elsewhere. I discovered the book whilst surfing the internet for recommendation engines, which itself was a spur from reading about a recommendation engine that Joel Pobar had written in F#.

Hopefully this book will shed some insight on writing a sophisticated Web 2.0 application.


NHibernate in Action - Various

NHibernate in Action - Various

Another re-purchase of a book I own in digital form. I have followed it through the Manning Early Access Program.

My main gripe is that it doesn't cover the newest version of NHibernate, but given the time it takes to write a book and the speed at which frameworks evolve it's forgivable.

One of the best references for NHibernate available today.


Other

Here Comes Everybody - Clay Shirky

Here Comes Everybody - Clay Shirky

If I had a dollar for every time I heard Jeff Atwood mention Clay Shirky on the StackOverflow podcast I'd probably be able to buy this book without opening my wallet! Instead, I decided to spend my own money and discover the material for myself.

It comes highly rated and promises some insight on the huge changes we are seeing on the internet today with social networking and the wisdom (or madness) of crowds.


The Paradox of Choice: Why More is Less - Barry Schwartz

The Paradox of Choice: Why More is Less - Barry Schwartz

This is the least technical book of the bunch, cited as a reference in The Cult of the Amateur - a book I really enjoyed reading on my travels.

I would probably consider myself a 'maximiser' when it comes to making purchases - perhaps this book will change the way I rationalise my spending decisions? Perhaps it will change the way I buy books? :)


Simply put, read books and treat your education as a #1 priority.

Bill Hicks would probably agree (just with more swearing):

Tags: , , ,

Categories: .NET | Books | C Sharp | General | MVC | SOA | Web

A Day In The Life Of A Software Recruiter

03 June 2009 by Stuart Cam

I have recently returned from a fairly long stay and tour around Australia and New Zealand. Since returning to sunny England my original plans have changed, I am now looking for contract web-based C# work in Bristol (drop me a line if you have a problem that needs solving).

I've updated my LinkedIn profile and uploaded my CV onto a couple of job boards on the off chance that some work might materialise through that channel. I am starting to wish I hadn't bothered.

Cut to a recent phone call.

...

Recruiter: So I see in your CV that you can program in jQuery?

Me: Sure, I have experience in using jQuery to write AJAX-enabled web pages and...

Recruiter (interrupting): AJAX?

Me: Asynchronous JavaScript And XML, it's a building block for creating rich internet applications...

OVERHEARD SCRIBBLING

Recruiter: OK... go on...

Me: ...so I have used jQuery for a little over a year. I have read a couple of books including jQuery In Action which was written by one of the framework contributors. I understand the CSS-selector syntax, the intentions of the framework and common use-cases. I have commercial experience using jQuery but I am no means a guru. As an aside, I wrote a similar intentioned framework for an e-commerce website around 8 years ago, but since then things have moved on and frameworks like jQuery have filled this space.

Recruiter: So... jQuery... it's a yes?

Me (sighing): Yes.

YOU COULD ALMOST HEAR THE PEN TICKING THE BOX

Recruiter: HTML experience?

...

I'm afraid to say that this scenario has played out over and over again for the last few weeks in different guises with many different recruiters. A tip to recruiters out there - if my CV states a particular technology and you ask me about it don't be surprised if I elaborate! Otherwise why even bother asking the question?

This brings me onto an imagined day in the life of a software professional recruiter:

  1. Wake up. Drink at least a dozen espressos in order to increase the number of words vocalised per minute. You'll need this for #7
  2. Trawl various job sites for CVs
  3. Copy CVs into the company system, why pay to access up-to-date CVs when you can copy and keep them forever? Quantity is everything, work the numbers!
  4. Receive word that Employer-X is looking for candidates, or more likely glean this information from other candidates
  5. Filter suitable keywords from the job specification. A useful tip here is to pick words where all the letters are capitalised. C#, .NET, C#, XML and ASP are such keywords
  6. Search the local pool of CVs using said keywords
  7. Call matching candidates
  8. Match keywords to candidate responses using a checklist
  9. Attempt to gain additional information from the candidate through such questions as:
    • Where else are they interviewing? (See #4)
    • Their theoretical lowest possible rate?
    • How are they finding the market? (what sort of question is this, do they not know their own market?!)
  10. Repeat #6 until candidate list is empty. Remember, volume and turnover is king
  11. Assemble all matching CVs into a big fire-and-forget e-mail to Employer-X
  12. Repeat #2 until 5PM
  13. Retreat to the cave for another nights sleep

Is it any wonder why the success rate of finding work through a job board has such a poor track record?

The problem is that the majority of recruiters finding and screening candidates for software development roles have little, if any, understanding of software development.

Think about that for a minute. Would you be happy if your local NHS service entrusted the hiring of staff to an agency with no experience in the industry? Would you hire a wedding planner to find you a reliable plumber? Probably not, it doesn't make sense, yet this is the reality today.

These fly-by-night recruiters are adding zero value in the employment chain. At best they are simply getting in the way and getting paid rather handsomely in the process. The situation is even more ridiculous when you consider that some companies sign exclusivity agreements with these clowns, forcing all potential candidates through the gauntlet. How does restricting your available talent pool in a knowledge-worker industry ever make sense for a business? It doesn't.

I take some comfort in knowing that I am not alone in thinking this way. Miguel Castro, a well respected member of the programming community rates sub-standard recruiters as one of the second biggest things which annoy him.

The financial climate today is such that recruiters themselves have resorted to sending their own CV's to other recruiters. A room full of recruiters schmoozing and interviewing each other for their own jobs, how cannibalistic!

I must point out that not all recruiters are like this. Some are actually very good and even generate work for candidates through their existing client relationships. I also appreciate that there is a time and a place where an external recruitment agency makes sense. In any industry you have the high-flyers and the bottom-feeders - how unfortunate for us professionals that our recruitment industry has so many of the latter.

Tags: , , , ,

Categories: .NET | General | Web


© Codebrain 2017. All Rights Reserved. Registered in England: 07744920. VAT: GB 119 4078 13