Create mobile apps with HTML5, JavaScript and Visual Studio

(Elle) #1

msdnmagazine.com February 2014 75


performed to create the corresponding delegate. Finally, we can’t


support any other Task type, as we wouldn’t know how to create it,


so a no-op delegate that just returns the original task is returned.


Th is behavior can now be used on an intercepted object and


will log the results of the tasks returned by the intercepted object’s


methods for the cases where a value is returned and where an


exception is thrown. Th e example in Figure 9 shows how a container


can be confi gured to intercept an object and use this new behavior,


and the resulting output when diff erent methods are invoked.


Covering Our Tracks


As you can see in the resulting output in Figure 9, the approach


used in this implementation results in a light change in the excep-


tion’s stack trace, refl ecting the way the exception was rethrown


when awaiting for the task. An alternative approach can use the


ContinueWith method and a TaskCompletionSource instead


of the await keyword to avoid this issue, at the expense of having a


more complex (and potentially more expensive) implementation


such as what’s shown in Figure 10.


Wrapping Up


We discussed several strategies for intercepting asynchronous


methods and demonstrated them on an example that logs the


completion of asynchronous operations. You can adapt this


sample to create your own intercepting behaviors that would sup-


port asynchronous operations. Full source code for the example


is available at msdn.microsoft.com/magazine/msdnmag0214. Q


FERNANDO SIMONAZZI is a soft ware developer and architect with more than 15 years


of professional experience. He has been a contributor to Microsoft patterns &


practices projects, including several releases of the Enterprise Library, Unity,


CQRS Journey and Prism. Simonazzi is also an associate at Clarius Consulting.


DR. GRIGORI MELNIK is a principal program manager on the Microsoft patterns


& practices team. Th ese days he drives the Microsoft Enterprise Library, Unity,


CQRS Journey and NUI patterns projects. Prior to that, he was a researcher


and soft ware engineer long enough ago to remember the joy of programming in


Fortran. Dr. Melnik blogs at blogs.msdn.com/agile.


THANKS to the following technical expert for reviewing this article:


Stephen Toub (Microsoft )


private readonly ConcurrentDictionary<Type, Func<Task, IMethodInvocation, Task>>
wrapperCreators = new ConcurrentDictionary<Type, Func<Task,
IMethodInvocation, Task>>();

private Func<Task, IMethodInvocation, Task> GetWrapperCreator(Type taskType)
{
return this.wrapperCreators.GetOrAdd(
taskType,
(Type t) =>
{
if (t == typeof(Task))
{
return this.CreateWrapperTask;
}
else if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Task<>))
{
return (Func<Task, IMethodInvocation, Task>)this.GetType()
.GetMethod("CreateGenericWrapperTask",
BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(new Type[] { t.GenericTypeArguments[0] })
.CreateDelegate(typeof(Func<Task, IMethodInvocation, Task>), this);

}
else
{
// Other cases are not supported
return (task, _) => task;
}
});
}

Figure 8 Implementing the GetWrapperCreator Method


using (var container = new UnityContainer())
{
container.AddNewExtension<Interception>();
container.RegisterType<ITestObject, TestObject>(
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<LoggingAsynchronousOperationInterceptionBehavior>());

var instance = container.Resolve<ITestObject>();

await instance.DoStuffAsync("test");

// Do some other work
}

Output:
vstest.executionengine.x86.exe Information: 0 :
Successfully finished async operation DoStuffAsync with value: test

vstest.executionengine.x86.exe Warning: 0 :
Async operation DoStuffAsync threw:
System.InvalidOperationException: invalid
at AsyncInterception.Tests.AsyncBehaviorTests2.TestObject.<
DoStuffAsync>d__38.MoveNext() in d:\dev\interceptiontask\
AsyncInterception\ AsyncInterception.Tests\
AsyncBehaviorTests2.cs:line 501
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess( Task task)
at System.Runtime.CompilerServices.TaskAwaiter.
HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at AsyncInterception.LoggingAsynchronousOperationInterceptionBehavior.<
CreateWrapperTask>d__3.MoveNext() in d:\dev\interceptiontask\
AsyncInterception\AsyncInterception\
LoggingAsynchronousOperationInterceptionBehavior.cs:line 63

Figure 9 Confi guring a Container to Intercept an Object and


Use the New Behavior


private Task CreateWrapperTask(Task task, IMethodInvocation input)
{
var tcs = new TaskCompletionSource<bool>();

task.ContinueWith(
t =>
{
if (t.IsFaulted)
{
var e = t.Exception.InnerException;
Trace.TraceWarning("Async operation {0} threw: {1}",
input.MethodBase.Name, e);
tcs.SetException(e);
}
else if (t.IsCanceled)
{
tcs.SetCanceled();
}
else
{
Trace.TraceInformation("Successfully finished async operation {0}",
input.MethodBase.Name);
tcs.SetResult(true);
}
},
TaskContinuationOptions.ExecuteSynchronously);

return tcs.Task;
}

Figure 10 Using ContinueWith instead of the Await Keyword

Free download pdf