• Stars
    star
    104
  • Rank 330,604 (Top 7 %)
  • Language
    C#
  • License
    MIT License
  • Created over 12 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An extension for Fody to integrate error handling into async and TPL code

AsyncErrorHandler.Fody

Chat on Gitter NuGet Status

Fody.AsyncErrorHandler is a Fody extension for weaving exception handling code into applications which use async code.

This is an add-in for Fody

It is expected that all developers using Fody become a Patron on OpenCollective. See Licensing/Patron FAQ for more information.

Usage

See also Fody usage.

NuGet package

https://nuget.org/packages/AsyncErrorHandler.Fody/

PM> Install-Package Fody
PM> Install-Package AsyncErrorHandler.Fody

The Install-Package Fody is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.

Add to FodyWeavers.xml

Add <AsyncErrorHandler/> to FodyWeavers.xml

<Weavers>
  <AsyncErrorHandler/>
</Weavers>

Why?

Because writing plumbing code is dumb and repetitive.

How?

IL-weaving after the code is compiled, bro.

For example, imagine you've got this code to serialize an object to the filesystem:

public class DataStorage
{
    public async Task WriteFile(string key, object value)
    {
        var jsonValue = JsonConvert.SerializeObject(value);
        using (var file = await folder.OpenStreamForWriteAsync(key, CreationCollisionOption.ReplaceExisting))
        using (var stream = new StreamWriter(file))
            await stream.WriteAsync(jsonValue);
    }
}

After the code builds, the weaver could scan your assembly looking for code which behaves a certain way, and rewrite it to include the necessary handling code:

public class DataStorage
{
    public async Task WriteFile(string key, object value)
    {
        try 
        {
            var jsonValue = JsonConvert.SerializeObject(value);
            using (var file = await folder.OpenStreamForWriteAsync(key, CreationCollisionOption.ReplaceExisting))
            using (var stream = new StreamWriter(file))
                await stream.WriteAsync(jsonValue);
        }
        catch (Exception exception)
        {
            AsyncErrorHandler.HandleException(exception);
        } 
    }
}

And your application could provide its own implementation of the error handling module:

public static class AsyncErrorHandler
{
    public static void HandleException(Exception exception)
    {
        Debug.WriteLine(exception);
    }
}

Which allows you to intercept the exceptions at runtime.

What it really does

So the above example is actually a little misleading. It shows "in effect" what is inject. In reality the injected code is a little more complicated.

What async actually produces

So given a method like this

public async Task Method()
{
    await Task.Delay(1);
}

The compile will produce this

[AsyncStateMachine(typeof(<Method>d__0)), DebuggerStepThrough]
public Task Method()
{
    <Method>d__0 d__;
    d__.<>4__this = this;
    d__.<>t__builder = AsyncTaskMethodBuilder.Create();
    d__.<>1__state = -1;
    d__.<>t__builder.Start<<Method>d__0>(ref d__);
    return d__.<>t__builder.Task;
}

So "Method" has become a stub that calls into a state machine.

The state machine will look like this

[CompilerGenerated]
struct <Method>d__0 : IAsyncStateMachine
{
    // Fields
    public int <>1__state;
    public Target <>4__this;
    public AsyncTaskMethodBuilder <>t__builder;
    private object <>t__stack;
    private TaskAwaiter <>u__$awaiter1;

    // Methods
    private void MoveNext();
    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine param0);
}

The method we care about is MoveNext. It will look something like this

void MoveNext()
{
    try
    {
        TaskAwaiter awaiter;
        bool flag = true;
        switch (this.<>1__state)
        {
            case -3:
                goto Label_009F;

            case 0:
                break;

            default:
                awaiter = Task.Delay(1).GetAwaiter();
                if (awaiter.IsCompleted)
                {
                    goto Label_006F;
                }
                this.<>1__state = 0;
                this.<>u__$awaiter1 = awaiter;
                this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Target.<Method>d__0>(ref awaiter, ref this);
                flag = false;
                return;
        }
        awaiter = this.<>u__$awaiter1;
        this.<>u__$awaiter1 = new TaskAwaiter();
        this.<>1__state = -1;
    Label_006F:
        awaiter.GetResult();
        awaiter = new TaskAwaiter();
    }
    catch (Exception exception)
    {
        this.<>1__state = -2;
        this.<>t__builder.SetException(exception);
        return;
    }
Label_009F:
    this.<>1__state = -2;
    this.<>t__builder.SetResult();
}

Most of that can be ignored. The important thing to note is that it is swallowing exceptions in a catch. And passing that exception to a SetException method.

So when AsyncErrorHandler does its weaving it searches for SetException(exception); and then modifies the catch to look like this.

catch (Exception exception)
{
    this.<>1__state = -2;
    AsyncErrorHandler.HandleException(exception);
    this.<>t__builder.SetException(exception);
    return;
}

Icon

Icon courtesy of The Noun Project

More Repositories

1

Fody

Extensible tool for weaving .net assemblies
C#
4,325
star
2

Costura

Embed references as resources
C#
2,383
star
3

PropertyChanged

Injects INotifyPropertyChanged code into properties at compile time
C#
1,885
star
4

NullGuard

Adds null argument checks to an assembly
C#
686
star
5

MethodTimer

Injects some very basic method timing code.
C#
686
star
6

Home

The landing page for Fody repositories
C#
671
star
7

ConfigureAwait

Configure async code's ConfigureAwait at a global level
C#
444
star
8

MethodDecorator

Compile time decorator pattern via IL rewriting
C#
380
star
9

Anotar

Simplifies logging through a static class and some IL manipulation.
C#
262
star
10

Equals

Generate Equals, GetHashCode and operators methods from properties.
C#
111
star
11

FodyAddinSamples

A working sample for each Fody Addin
C#
95
star
12

ToString

Generate ToString method from public properties.
C#
82
star
13

Janitor

Simplifies the implementation of IDisposable
C#
76
star
14

Ionad

Replaces static method calls.
C#
68
star
15

InfoOf

Provides methodof, propertyof and fieldof equivalents of typeof .
C#
65
star
16

Virtuosity

Change all members to virtual as part of your build.
C#
61
star
17

PropertyChanging

Injects INotifyPropertyChanging code into properties at compile time.
C#
54
star
18

ExtraConstraints

Facilitates adding constraints for Enum and Delegate to types and methods.
C#
47
star
19

Visualize

Adds debugger attributes to help visualize objects.
C#
43
star
20

Caseless

Change string comparisons to be case insensitive.
C#
40
star
21

Resourcer

Simplifies reading embedded resources from an Assembly.
C#
37
star
22

LoadAssembliesOnStartup

Loads all the references on startup by actually using the types in the module initializer.
C#
36
star
23

Obsolete

Helps keep usages of ObsoleteAttribute consistent.
C#
32
star
24

EmptyConstructor

Adds an empty constructor to classes even if you have a non-empty one defined.
C#
29
star
25

Scalpel

Strips all testing code from an assembly
C#
27
star
26

AssertMessage

Add 'message' parameter to Assertions. Nunit, Mstest, Xunit is supported.
C#
22
star
27

Publicize

Converts non-public members to public hidden members
C#
12
star
28

UniversalAppSample

C#
7
star
29

.github

1
star