In ASP.Net Core, come si risolvono i riferimenti circolari?

5

Stavo lavorando alla migrazione su un progetto che utilizza un logger statico e un servizio di posta elettronica statico.
Il servizio di posta elettronica registra le e-mail inviate e il servizio di registrazione invia una e-mail in caso di problemi di registrazione (come il down di DB, ecc.). Se il servizio di posta elettronica non riesce, può anche registrare il problema.

Se il logger non funziona, tenta di inviare una notifica email, ma se il servizio di posta elettronica non riesce (il motivo potrebbe essere il motivo per cui il logger è stato chiamato in primo luogo), continua e scrive il problema su un file di registro.

Email Service <--statically references--> Logger Service

Il problema è che entrambi i "servizi" sono singleton, ed entrambi avrebbero bisogno di fare riferimento l'un l'altro come parametro costruttore. Il mio primo pensiero è stato l'iniezione basata sulla proprietà, ma ho scoperto rapidamente che sembra essere una situazione di accoppiamento temporale molto indesiderata.

L'idea principale ora è, a livello di log, chiamare il fornitore di servizi per creare un servizio di posta elettronica (così otterrà l'istanza singleton), ma stavo davvero cercando di mantenere la libreria di classi disaccoppiata dal . Sistema di iniezione Core. Qualcuno ha qualche raccomandazione nel caso mi manchi qualcosa?

Modifica: Dopo un po 'di riflessione, ha senso forse creare un fornitore di servizi astratto (uno di base) che può avere un altro servizio iniettato in esso? Allora potrei forse passarlo in giro in questi casi.

    
posta James Wilkins 11.03.2017 - 08:38
fonte

2 risposte

5

Ti suggerisco di leggere un ottimo libro su C# iniezione di dipendenza di Mark Seemann, con molti campioni del mondo reale per diversi compiti che puoi fare con esso, anche con dipendenze circolari simili al tuo caso.

In generale, la regola di progettazione principale è You can't resolve the problem on its level , ed anche in questo caso è vero. Hai davvero bisogno di un mediatore qui per risolvere le interazioni tra due logger. Può facilmente salvare lo stato per entrambi e informarsi a vicenda sulle modifiche.

Ad esempio, puoi introdurre un po 'di Target per i messaggi, in questo modo:

GeneralMessage // both loggers got this
EmailMessage // only email logger got this
LoggerMessage // only logger got this

Quindi, in caso di problemi con uno dei tuoi logger, il mediatore invia il messaggio con il tipo corrispondente per notificare altri problemi.

L'approccio comune per tale logica all'avanguardia è un'implementazione AOP (personalmente preferisco il PostSharp , che ti fornisce un approccio generale, anche con tecniche convenzionali per assegnare gli aspetti alle tue classi).

Per quanto riguarda il disaccoppiamento del codice dal sistema di iniezione .Net Core , ti suggerisco di esaminare Logger Factory , che è:

The new framework creates an abstraction layer or wrapper that enables you to use whichever logging framework you want as a provider. This ensures you have the maximum flexibility in your work as a developer. Furthermore, even though it’s only available with .NET Core, referencing .NET Core NuGet packages like Microsoft.Extensions.Logging for a standard Visual Studio .NET 4.6 project is no problem.

Con Logger Factory puoi anche filtrare i messaggi per notificare a tutti i logger i problemi con altri, collegati ai registri eventi del sistema operativo e molto altro ancora.

    
risposta data 12.03.2017 - 08:12
fonte
2

Non riesco a capire se desideri mantenere la dipendenza circolare ma gestisci errori di riferimento circolari o se chiedi un'alternativa alla dipendenza circolare.

Se ti piace la dipendenza circolare ma non riesci a capire come implementarla, puoi risolvere il problema con Unity, come descritto in questo articolo .

Se non ti piace la dipendenza circolare (non mi piace neanche) allora ecco alcune idee:

  1. Sbarazzati dei singleton. Mentre quel modello era popolare nel 2005, in questi giorni è considerato veramente cattivo . Terribile per il test delle unità, in particolare.

  2. Il tuo servizio di registrazione non dovrebbe inviare email. Dovrebbe consentire la possibilità di fare una cosa: scrivere nel registro. Questo è tutto.

  3. Il tuo servizio di registrazione dovrebbe essere abbastanza semplice in modo che non fallisca mai. A meno che tu non abbia memoria, nel qual caso non sarai in grado di inviare neanche un'email.

  4. Se lo desideri, puoi avere un servizio separato che persiste nel registro. Ad esempio, esegui il commit su disco o invia un'email se lo desideri. Ma dovrebbe essere separato dal log writer.

  5. Se vuoi essere davvero fantasioso, puoi avere diversi servizi che possono persistere nel log, usando schema della catena di responsabilità . Ciò consente la capacità di failover. Ad esempio, forse i log vengono mantenuti nel database; se la connessione SQL fallisce, vengono memorizzati in un file flat; se il file flat è pieno, invia un'email. Forse puoi farlo in una versione successiva:)

Quanto sopra presuppone per "log" un "log di debug" di qualche tipo, al contrario di, ad esempio, un log di controllo, ad es. a fini regolamentari, che può essere mission critical e molto più complicato.

    
risposta data 11.03.2017 - 10:17
fonte

Leggi altre domande sui tag