Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Fehlerbehandlung mit Try/Catch innerhalb einer asynchronen Methode
Paschulke
myCSharp.de - Member



Dabei seit:
Beiträge: 63

Themenstarter:

Fehlerbehandlung mit Try/Catch innerhalb einer asynchronen Methode

beantworten | zitieren | melden

Hallo!

Ich habe eine Methode zum Laden eines Objekts, die asynchron aufgerufen werden soll.
Darin wird zunächst versucht das zu ladende Objekt aus einer Datei zu deserialisieren. Wenn das fehlschlägt, soll das Objekt aus einer Datenbank gelesen werden.

Hier mal ein auf die wesentlichen Aspekte rediziertes Beispiel:


public void Main()
{
    MyObject myObject = LoadMyObjectAsync().Result();

    // myObject ist null, wenn der Code durch den catch-Block läuft.
}

private async Task<MyObject> LoadMyObjectAsync()
{
    try
    {
        return await DeserialzeMyObjectAsync().ConfigureAwait(false);
    }
    catch (SerializationException)
    {
        return await db.ReadMyObjectAsync().ConfigureAwait(false);
    }
}

Stellt mal außer Frage, ob das Beispiel in der Form Sinn macht. Mir geht es darum, die Technik zu verstehen...

Ich habe festgestellt, dass innerhalb der Methode DeserialzeMyObjectAsync ein Thread erzeugt wird, der geschlossen wird, wenn der Code seinen erwarteten Weg geht. Wenn der Code in die Exception läuft, bleibt der Thread auch nach der Rückkehr in die wartende Methode bestehen. Deshalb ist myObject im aufrufenden Thread null.

Wie fange ich innerhalb der Methode "LoadMyObjectAsync" den Fehler korrekt ab? Bzw. wie fahre ich im ExceptionHandler korrekt fort?
private Nachricht | Beiträge des Benutzers
Deaktiviertes Profil
myCSharp.de - Member



Dabei seit:
Beiträge: 996

beantworten | zitieren | melden

Probiere es doch mal mit einem Minimal-Programm aus


    class Program
    {
        static void Main( string[] args )
        {

            object deserializerObject = null; // new object();
            IDeserializer deserializer = new DummyDeserializer( deserializerObject );
            object readerObject = new object();
            IReader reader = new DummyReader( readerObject );

            var loader = new Loader( deserializer, reader );
            var result = loader.LoadMyObjectAsync().Result;

            if ( result == deserializerObject ) Console.WriteLine( "object from Deserializer" );
            if ( result == readerObject ) Console.WriteLine( "object from Reader" );
        }

        class DummyReader : IReader
        {
            private object returnObject;
            public DummyReader( object returnObject )
            {
                this.returnObject = returnObject;
            }
            public object ReadMyObject()
            {
                if ( returnObject == null ) throw new ReaderException();
                return returnObject;
            }

            public Task<object> ReadMyObjectAsync()
            {
                return Task.Run( () =>
                {
                    if ( returnObject == null ) throw new ReaderException();
                    return returnObject;
                } );
            }
        }

        class DummyDeserializer : IDeserializer
        {
            private object returnObject;

            public DummyDeserializer( object returnObject )
            {
                this.returnObject = returnObject;
            }

            public object DeserializeMyObject()
            {
                if ( returnObject == null ) throw new SerializationException();
                return returnObject;
            }

            public Task<object> DeserializeMyObjectAsync()
            {
                //return Task.FromResult( deserializerObject );
                return Task.Run( () =>
                {
                    if ( returnObject == null ) throw new SerializationException();
                    return returnObject;
                } );
            }
        }

        class SerializationException : Exception
        {
        }

        class ReaderException : Exception
        {
        }

        interface IDeserializer
        {
            object DeserializeMyObject();
            Task<object> DeserializeMyObjectAsync();
        }

        interface IReader
        {
            object ReadMyObject();
            Task<object> ReadMyObjectAsync();
        }

        class Loader
        {
            private IDeserializer deserializer;
            private IReader reader;

            public Loader( IDeserializer deserializer, IReader reader )
            {
                this.deserializer = deserializer;
                this.reader = reader;
            }

            public object LoadMyObject()
            {
                try
                {
                    return deserializer.DeserializeMyObject();
                }
                catch ( SerializationException )
                {

                    return reader.ReadMyObject();
                }
            }

            public async Task<object> LoadMyObjectAsync()
            {
                try
                {
                    return await deserializer.DeserializeMyObjectAsync();
                }
                catch ( SerializationException )
                {

                    return await reader.ReadMyObjectAsync();
                }
            }
        }
    }
Daraus könntest du jetzt auch einen Unit-Test zusammenbauen, der dir das gewünschte Verhalten auch testet.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Deaktiviertes Profil am .
private Nachricht | Beiträge des Benutzers
RED-BARON
myCSharp.de - Member



Dabei seit:
Beiträge: 76

beantworten | zitieren | melden

mit c# 6 sollte es funktionieren. Ansonsten nicht.
https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status&referringTitle=Documentation
private Nachricht | Beiträge des Benutzers
Deaktiviertes Profil
myCSharp.de - Member



Dabei seit:
Beiträge: 996

beantworten | zitieren | melden

Ach darum geht es ...


public void Main()
{
    MyObject myObject = LoadMyObjectAsync().Result();

    // myObject ist null, wenn der Code durch den catch-Block läuft.
}

private async Task<MyObject> LoadMyObjectAsync()
{
    try
    {
        return await DeserialzeMyObjectAsync().ConfigureAwait(false);
    }
    catch (SerializationException)
    {
        // return await db.ReadMyObjectAsync().ConfigureAwait(false);
    }
    return await db.ReadMyObjectAsync().ConfigureAwait(false);
}
private Nachricht | Beiträge des Benutzers
RED-BARON
myCSharp.de - Member



Dabei seit:
Beiträge: 76

beantworten | zitieren | melden

glaub nicht, schau mal hier
http://stackoverflow.com/questions/16626161/a-good-solution-for-await-in-try-catch-finally


static async Task f()
{
    ExceptionDispatchInfo capturedException = null;
    try
    {
        await TaskThatFails();
    }
    catch (MyException ex)
    {
        capturedException = ExceptionDispatchInfo.Capture(ex);
    }

    if (capturedException != null)
    {
        await ExceptionHandler();

        capturedException.Throw();
    }
}

vor c#6 braucht man async/await wohl kaum verwenden
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15703
Herkunft: BW

beantworten | zitieren | melden

Das Problem ist die falsche Handhabe mit Result. Darüber hinaus würde ich auch sagen, dass dieser Code keine gute Umsetzung im Allgemeinen darstellt.

Result sollte am besten gar nicht verwendet werden, weil es Deadlocks auslösen kann und Exceptions wrappt.

Dass async/await vor C# 6 nicht nutzbar ist, ist natürlich auch quatsch.
private Nachricht | Beiträge des Benutzers
Paschulke
myCSharp.de - Member



Dabei seit:
Beiträge: 63

Themenstarter:

beantworten | zitieren | melden

Oh je... Asche auf mein Haupt!
Ich habe jetzt 2 Tage mit diesem Problem verbracht. Der schlichte, blöde Fehler war einfach nur, dass ich vergessen hatte den Rückgabewert zuzuweisen. Wie blind kann man sein...! Vor allem nachdem ich den Code noch einmal vereinfacht (das war hier mein Fehler!) hier rein gesetzt hatte...

Ich hatte also sinngemäß anstatt...


catch
{
    myObject = await db.ReadMyObjectAsync().ConfigureAwait(false);
}
return myObject;
einfach nur...


catch
{
    await db.ReadMyObjectAsync().ConfigureAwait(false);
}
return myObject;
geschrieben.

Wenn es hier eine Kaffeekasse für Dämlichkeit gäbe würde ich jetzt einen 5er rein schmeißen!

Sorry für die falsch gestellte Frage!!! Aber Eure Beiträge (vor allem der Beispielcode) haben mir trotzdem sehr geholfen. Einfach um einen anderen Blick auf das Problem zu bekommen...
private Nachricht | Beiträge des Benutzers