8 minute read

 

If you are new to dI.Hook, please visit the di.Hook Product Page for Overview, Release Version and Source Code.

Some posts you might want to read would be:

  1. How to create a HookRepository?
  2. How to add hooks to HookRepository?
  3. How to add hooks via configuration file to a HookRepository?

So this is the fourth article in the series which will deal with invoking all the hooks in the repository.  This article will be short and simple as there are no two ways of invoking all the hooks in a repository.

Step 1 – Creating a repository and add hooks into a repository

 

The first decision in creating a hook repository is to think whether you want to have lazy loading or not.  Once you have decided you can go through the steps – Creating a Hook Repository using dI.Hook

Be selective in adding hooks into a repository if you plan to invoke all of them at one go.  If you are thinking of manually adding hooks to a repository, would suggest going through – Adding hooks manually into a repository.  However, if you are planning to load them through configuration you can read – Loading a configuration set of hooks into repository

Step 2 – Invoking all the hooks in the repository

 

The code is fairly simple.  In fact, just one line

Invoking all hooks
int invokedCount = hookRepository.InvokeAll();

 

The above code will call OnInvoke() of all the hooks without any parameters.  But how often would you have a scenario when you would have a hook with no parameters? I mean a system that does not take any input and gives no output. A black box! Oh, I haven’t seen such systems.  Most of the complexity in systems involve either communication between other systems in the ecosystem or in executing complex logic involving lot of workflows and rules.

So hooks with no inputs and no outputs – not useful in many scenarios!

InvokeAll with Parameters
int invokedCount = hookRepository.InvokeAll("CalledByTestWithDifferentValues", 2);

 

More parameters – not an issue!  You can add N-parameters each of different type and it would work flawlessly!

So far, so good!

But this code does not necessarily is performance centric. I mean, it does not really leverage the benefit of multi-core 64-bit processor and higher gig-RAM.  So we must optimize this to have some kind of parallel execution and spawning of threads. With .NET 4.0, we can ease this task using TPL – Read Parallel Programming in .NET 4.0 for more statistics.  So here’s the version that executes invoke of all hooks in parallel.

InvokeAllAsParallel Example
int invokedCount = hookRepository.InvokeAllAsParallel("CalledByTestWithDifferentValues", 2);

Now that works great!

OKay, one last word before we reach to the end of this article.  Now all invoke methods return an Int32 value.  This Int32 value represents the number of hooks whose OnInvoke executed successfully!

Does it mean that there are chances that not all hooks will be executed? Yes.  Let’s see that taking some examples

Invoke - With Exceptions
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Test_Standard_InvokeAllWithExceptionRaisedInFirstHook()
{
    // should block diagnostichook as well and throw exception
    int invokedCount = hookRepository.InvokeAll("RaiseLogException");
    Assert.AreEqual(0, invokedCount);
}

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Test_Standard_InvokeAllWithExceptionRaisedInSecondHook()
{
    // should not block LogHook but should throw exception
    int invokedCount = hookRepository.InvokeAll("RaiseDiagnosticException");
    Assert.AreEqual(1, invokedCount);
}

In the method InvokeAllWithExceptionRaisedInFirstHook, assume that the LogHook throws an exception.  In the above case since DiagnosticsHook was added in the repository after LogHook (refer to article on How to Add Hooks), DiagnosticsHook is not called.  Hence the invokedCount = 0

In the mehod InvokeAllWithExceptionRaisedInSecondHook, the LogHook does not throw an exception but DiagnosticsHook does.  Hence only LogHook gets executed leaving the value of invokedCount = 1

Now lets see what happens when we use InvokeAsParallel

InvokeAsParallel - Exceptions
[TestMethod]
[ExpectedException(typeof(AggregateException))]
public void Test_Standard_InvokeAllAsParallelWithExceptionRaisedInFirstHook()
{
    // will not block diagnostichook as it is parallel and will throw exception
    int invokedCount = hookRepository.InvokeAllAsParallel("RaiseLogException");
    Assert.AreEqual(1, invokedCount);
}

[TestMethod]
[ExpectedException(typeof(AggregateException))]
public void Test_Standard_InvokeAllAsParallelWithExceptionRaisedInSecondHook()
{
    // will not block LogHook as it is parallel but should throw exception
    int invokedCount = hookRepository.InvokeAllAsParallel("RaiseDiagnosticException");
    Assert.AreEqual(1, invokedCount);
}

 

In the method InvokeAllWithExceptionRaisedInFirstHook, assume that the LogHook throws an exception. In the above case since DiagnosticsHook was added in the repository after LogHook (refer to article on How to Add Hooks) but it is executed in parallel, hence DiagnosticsHook is invoked.  This definitely means that an AggregateException will be thrown to calling method. Hence the invokedCount = 1

In the mehod InvokeAllWithExceptionRaisedInSecondHook, the LogHook does not throw an exception but DiagnosticsHook does. Hence only LogHook gets executed leaving the value of invokedCount = 1

To summarize, InvokeAll() method executes all hooks sequentially.  If any one fails, the following ones do not get executed.  InvokeAllAsParallel() method executes all hooks in parallel.  If any one fails, others still get executed.  Both methods will throw different exception messages to the calling method.

In next article, we will explore how to invoke only specific hooks.