myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns
» Datenschutzerklärung
» Impressum

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Rund um die Programmierung » Error handling im CQRS Pattern (MediatR) (asp.net core)
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Error handling im CQRS Pattern (MediatR) (asp.net core)

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Olii
myCSharp.de-Mitglied

Dabei seit: 20.09.2017
Beiträge: 66


Olii ist offline

Error handling im CQRS Pattern (MediatR) (asp.net core)

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo liebe User,

bislang habe ich mich immer vor diesem Thema gedrückt, aber nun will ich da mal richtig ran. Einen Error zu handeln ist meist ja gar nicht das Problem, sonder einen Error richtig zu handeln oder diese entsprechend abzufangen.

Was wäre der korrekte weg in dem SQRS Pattern im handle Fehler richtig zu behandeln?

Ein kleines Beispiel:

Eine Funktion zum registrieren eines Nutzers. In dieser Funktion wird geprüft ob es einen user mit dem selben Namen schon einmal gibt. Wenn nein, dann registriere den Nutzer und gib seine ID zurück, wenn ja, dann gib einen leeren Guid zurück (alle ID's sind guids).

Ein leerer Guid ist aber ein valider Guid. Wie könnte man in solchen Fällen am besten vorgehen?

C#-Code:
public async Task<Guid> Handle(CommandRegister command, CancellationToken cancellationToken)
    {
        List<Condition> ConditionList = new List<Condition>();
        ConditionList.Add (new Condition("username","=",command.username, DbType.String));
        ConditionList.Add (new Condition("email","=",command.email, DbType.String));
        if (_baseCommandRepository.GetSingle<int>("Count(*)","applicationuser",ConditionList) > 0)
        {
            return Guid.Empty; //ToDo leerer guid ist trotzdem ein valider guid
        }

        return await Task.Run(() => _baseCommandRepository.InsertReturnGuid<CommandRegister>("applicationuser",command, "iduser"));
    }

Ich habe mir ein paar Möglichkeiten überlegt:

1. Ich prüfe in der Funktion darüber ab ob der return value == Guid.Empty

2. Ich splitte die Funktion in zwei und gebe einen bool zurück bei der Überprüfung ob ein User existiert. Und anhand dessen kann ich dann ein z.B. BadRequest() an den Client senden. Müsste aber die commands etc. kopieren.

3. oder ich prüfe auf den string "0000-0000-0000-0000" ab, was aber eigentlich wie Möglichkeit Nr. 1 ist

4. Man könnte den User aber auch im ungewissen lassen und ihm bei Fehlern gar nicht bescheid geben

Aber keiner dieser Möglichkeiten scheint mir elegant genug.
Ich möchte nicht einfach programmieren lernen... Ich will es richtig lernen und es gut machen.

Falls jemand Vorschläge hat, würde ich mich sehr freuen oder auch auf eine rege Diskussion :)
04.02.2019 21:15 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 12.785
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Errorhandling allgemein:
CQRS wird Dir diese Frage an für sich nicht beantworten, sondern die Implementierung.
Der CQRS Pattern selbst kennt nur eine Kommunikationsrichtung; und in diese gehen auch Exceptions. Im Falle von IMediatR, also einer sehr vereinfachten In-Process Implementerung, kannst Du ganz normal mit Exceptions und Rückgaben arbeiten.
Du hast hier aber kein wirkliches Errorhandling im Sinne von Exceptions - sondern nur Logik.

Was ist denn das für eine Message? CreateUserMessage? Oder nur Exists-Prüfung?

PS: Der Code ist wirklich sehr unsauber.
Eine riesige Menge an Magic Strings -> unbedingt vermeiden; suboptimales async/await..
Vermutlich wäre bei einer korrekten Implementierung von DB und Logik der Fall gar nicht vorhanden.
Was für eine DB ist das denn? Wieso verwendest Du nicht ADO.NET? Oder ist das ein eigener Wrapper?

Punkt 4 darf niemals passieren.

Zitat von Olii:
Ich möchte nicht einfach programmieren lernen... Ich will es richtig lernen und es gut machen.

Es wird fast nie nur eine Lösung geben.

PPS: es ist eine Methode, keine Funktion.
05.02.2019 10:16 Beiträge des Benutzers | zu Buddylist hinzufügen
Olii
myCSharp.de-Mitglied

Dabei seit: 20.09.2017
Beiträge: 66

Themenstarter Thema begonnen von Olii

Olii ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Also diese Magic Strings habe ich gemacht weil ich versucht habe sowas wie ein SQL-Builder zu machen. Ich gebe Parameter rein und daraus wird mir ein SQL generiert und gegen die Datenbank gefeuert.

Der Grund wieso ich das machen wollte ist, dass ich nicht in jeden Handler den DBContext übergeben wollte sonder nur in diesen SQLBuilder. Ich dachte mir ads es ne gute Sache sei aber bin mir mitlerweile auch nicht mehr ganz so sicher weil es immer unleserlicher und relativ unflexible ist. Aber was anderes auser Magic Strings zu bauen ist mir nicht eingefallen.

Ich benutze kein ADO.NET weil ich DAPPER schon im Projekte hatte und dachte das DAPPER nicht schlecht ist. Also habe ich den benutzt. Oder ist Dapper eher ungeeignet?

Und als Datenbank verwende ich PostgreSQL.

Dieser Handler sollte überprüfen ob ein User in der Datenbank vorhanden ist. Wenn nein, hat er den User angelegt. An sich funktioniert das auch, aber ob die umsetzung schön ist ist eine andere Sache :D

Um ehrlich zu sein habe ich mich mit Asyn noch garnicht beschäftigt. Habe ich nur so schon mal eingebaut damit kein Fehler kommt.
06.02.2019 18:32 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 12.785
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Olii:
Ich gebe Parameter rein und daraus wird mir ein SQL generiert und gegen die Datenbank gefeuert.

Das geht auch problemlos und typsicher mit Linq (oder anderen Varianten, die auf Expression basieren).

Zitat von Olii:
Der Grund wieso ich das machen wollte ist, dass ich nicht in jeden Handler den DBContext übergeben wollte sonder nur in diesen SQLBuilder.

Damit raubst Du Dir Flexibilität.

Zitat von Olii:
Ich benutze kein ADO.NET weil ich DAPPER schon im Projekte hatte und dachte das DAPPER nicht schlecht ist. Also habe ich den benutzt. Oder ist Dapper eher ungeeignet?

Ich persönlich bin ein absoluter Fan von Dapper, wenn es um relationale Datenbanken geht.
Daher bleib ruhig bei Dapper!
Ich würde aber ein Repository Pattern davor setzen, um die Typsicherheit zu gewährleisten. Damit wärst Du auch effizienter in Sachen Wiederverwendbarkeit als diese SQLBuilder-Konstruktion.

Deine Handler bekommen dann ein Repository injiziert.

Zitat von Olii:
Dieser Handler sollte überprüfen ob ein User in der Datenbank vorhanden ist. Wenn nein, hat er den User angelegt. An sich funktioniert das auch, aber ob die umsetzung schön ist ist eine andere Sache :D

Dann würde ich hier keine Überprüfung im Code machen (Gefahr von Race Conditions), sondern einfach versuchen den User anzulegen.
Wenn der Context dann eine Exception wirft, dass zB die E-Mail bereits existiert, dann hast Du Race-Condition-Safe Deine Exist-Prüfung bereits.

Eine UserAlreadyExistException kannst Du anschließend in der Fehlerbehandlung des Handlers werfen.
06.02.2019 22:32 Beiträge des Benutzers | zu Buddylist hinzufügen
Olii
myCSharp.de-Mitglied

Dabei seit: 20.09.2017
Beiträge: 66

Themenstarter Thema begonnen von Olii

Olii ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Abt:
Ich würde aber ein Repository Pattern davor setzen, um die Typsicherheit zu gewährleisten. Damit wärst Du auch effizienter in Sachen Wiederverwendbarkeit als diese SQLBuilder-Konstruktion.

Also quasi verschachteln von beiden Pattern. Das hört sich interessant an. Das versuche mal.

Danke für die Tips Abt. Ich probiere mal die Dinge umzusetzen und gebe hier dann nochmal ein Kommentar dazu für Leute die es vielleicht interessiert.
06.02.2019 23:11 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 12.785
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Eine Software besteht i.d.R. immer aus einer Vielzahl von Pattern.

Im Prinzip kannst Du auch alles mit Handler abbilden und quasi die Handler kleiner schneiden - kann auch sinn machen.
Sodass Handler "CreateUser" eben ein "AddUserToDatabaseMessage" aufruft statt ein UserRepository.Add()

Müsste man die Gesamtsituation kennen. Tendiere aber zum Repositoryweg: der ist einfach simpler und erfüllt den Zweck genauso.

Aber kannst Du auch später ändern, sollte das notwendig sein.
Eine Architektur entwickelt sich wie alle anderen Teile einer Software schließlich auch weiter.
06.02.2019 23:15 Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 5 Monate.
Der letzte Beitrag ist älter als 5 Monate.
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 16.07.2019 08:38