Serwis dla generycznych powiadomień

Odpowiedz Nowy wątek
2019-07-17 10:07
0

Buduję aplikację konsolową która ma wysyłać żądania do Web API. Aplikacja ta będzie posiadać kilka serwisów odpowiedzialnych za:

  • pobranie danych przez Console.ReadLine() ,
  • wygenerowanie danych,
  • serializację danych,
  • migrację bazy i tabeli,
  • wywołanie żądań API.

Po każdej wykonanej czynności chcę żeby w konsoli się pojawił komunikat:

  • rozpoczęto coś tam,
  • zakończono coś tam,
  • rezultat jakiś tam,
  • stan jakiś tam, itp.

Generalnie to tych Console.WriteLine się trochę zbierze. Zastanawiam się czy znacie jakieś ciekawe generyczne rozwiązanie dla wywołań odpowiedniego stringu przy każdym komunikacie? Na razie przychodzi mi do głowy pomysł wykorzystujące słownik i wywołanie go z kluczem nazwy wywołującej metody wyciągniętej przez [CallerMemberName], jednak nie wiem jeszcze jak przeskoczyć fakt, że każda metoda może chcieć wyświetlić kilka komunikatów w zależności od etapu egzekucji i stanu.

edytowany 1x, ostatnio: bakunet, 2019-07-17 10:09

Pozostało 580 znaków

2019-07-17 10:11
2

Co jest złego w klasycznym Console.WriteLine() (bądź innych, bardziej logging-oriented rozwiązaniach)?

Takie komunikaty jest bajecznie prosto potem w kodzie odnaleźć, bajecznie prosto debugować i modyfikować... same zalety, w porównaniu do refleksyjnej magii.


edytowany 2x, ostatnio: Patryk27, 2019-07-17 10:12
Złego może nic, tylko się zastanawiałem czy jest opcja zebrania wszystkich komunikatów w jednym miejscu i wołania ich wg klucza - bakunet 2019-07-17 10:13

Pozostało 580 znaków

2019-07-17 10:17
2

jednak nie wiem jeszcze jak przeskoczyć fakt, że każda metoda może chcieć wyświetlić kilka komunikatów w zależności od etapu egzekucji i stanu.

Dictionary<string, Func>();

mozesz wtedy zrobic takie cos

a = new Dictionary<string, Func>();
a["bla"] = () => 
{
  Console.WriteLine("1");
  Console.WriteLine("2");
  Console.WriteLine("3");
  Console.WriteLine("4");
}

mogłem ze składnią się walnąc jako ze pisane z głowy

edytowany 1x, ostatnio: fasadin, 2019-07-17 10:17

Pozostało 580 znaków

2019-07-17 10:55

Kilka rozwiązań można tutaj zastosować:

  1. Wprost wrzucasz w kod Console.WriteLine() (lub jakiś inny logger NLog, log4net) - osobiście także nie lubię jak komunikaty przesłaniają to co metoda robi. Ale czasem idę na taki kompromis.
  2. Dictionary - tak jak zaproponowałeś.
  3. Idziesz w coś w rodzaju Domain Logging https://martinfowler.com/articles/domain-oriented-observability.html.
  4. Wyciągasz armatę i wybierasz Fody https://github.com/Fody/Fody.
Chya najbardziej wyczerpująca odpowiedź, dzięki :) - bakunet 2019-07-18 03:35

Pozostało 580 znaków

2019-07-17 13:47
0

Albo publikujesz eventy na kolejkę :D

Przecież to wcale nie zmienia rodzaju problemu ;-p - Patryk27 2019-07-17 14:21
ale można wpisać do CV :P - leggo 2019-07-17 14:23
Z eventami miałem do czynienia już, więc nie będę sobie komplikował niepotrzebnie życia :) Ciekawi mnie jedynie jak byś publikował je? Na początku bloku metody byś publikował jej nazwę? - bakunet 2019-07-18 03:34
tu z pomocą przychodzi programowanie aspektowe ;) - leggo 2019-07-18 08:06

Pozostało 580 znaków

2019-07-18 03:39
0

Ostatecznie zdecydowałem się wykorzystać dwa słowniki, jeden dla wiadomości, drugi dla błędu, wchodząc do nich nazwą metody z [CallerMemberName]. Największym minusem rozwiązania jest fakt, że przy zmianie nazwy metody muszę zmienić też string klucza w słownikach. Plusem jest zebranie komunikatów w jednym miejscu i mniejszy bałagan w metodzie.

        Dictionary<string, string> _messagesInfo;
        Dictionary<string, string> _messagesError;

I wokorzystując je tak:

public string SerializeData(List<PersonModel> dataCollection)
        {
            string callerName = _messageService.GetCallerName();
            string json = "";

            Console.Write(_messagesInfo[callerName]);

            try
            {
                if (dataCollection.Count != 0)
                {
                    json = JsonConvert.SerializeObject(dataCollection);
                }

                Console.WriteLine("OK");

                return json;
            }
            catch (Exception e)
            {
                Console.WriteLine(_messagesError[callerName]);

                return "";
            }
        }

Ale chcę dorzucić jeszcze NLog żeby zapisywał też komunikaty w pliku.

edytowany 1x, ostatnio: bakunet, 2019-07-18 03:45

Pozostało 580 znaków

2019-07-18 14:31
1
bakunet napisał(a):

Największym minusem rozwiązania jest fakt, że przy zmianie nazwy metody muszę zmienić też string klucza w słownikach.

Nie możesz użyć operatora nameof?


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
edytowany 1x, ostatnio: somekind, 2019-07-18 14:31
Wyjdzie na to samo jakbym użył [callerName] - bakunet 2019-07-18 15:09
Jak użyjesz callerName jako klucz słownika? - somekind 2019-07-18 15:13

Pozostało 580 znaków

2019-07-18 15:16
0
somekind napisał(a):

Jak użyjesz callerName jako klucz słownika?

List<string> _methodList;
        public MessageService() //ctor
        {
            _methodList = new List<string>()
            {
                "EnterTheQty",
                "GenerateRandomData",
                "SerializeData"
            };
        }
        public string GetCallerName([CallerMemberName] string caller = null)
        {
            return caller;
        }

        public Dictionary<string, string> GetMessagesError()
        {
            Dictionary<string, string> messages = new Dictionary<string, string>();
            messages.Add(_methodList[0], "\nError when getting the number.");
            messages.Add(_methodList[1], "\nError when generating random data.");
            messages.Add(_methodList[2], "\nError when serializing data.");

            return messages;
        }

        public Dictionary<string, string> GetMessagesInfo()
        {
            Dictionary<string, string> messages = new Dictionary<string, string>();
            messages.Add(_methodList[0], "\nHow many records you do want to generate? ");
            messages.Add(_methodList[1], "\nGenerating {0} models... ");
            messages.Add(_methodList[2], "\nSerializing collection to the JSON format... ");

            return messages;
        }

A pobieranie z serwisu poprzedni post

edytowany 2x, ostatnio: bakunet, 2019-07-18 15:18

Pozostało 580 znaków

2019-07-18 20:33
var
1

Plusem jest zebranie komunikatów w jednym miejscu i mniejszy bałagan w metodzie.

Jeśli twoja aplikacja ma szansę się rozrosnąć to możesz w przyszłości zmienić zdanie.
Patrząc na pierwszy post gdzie definiujesz 4 miejsca w których chciałbyś wyświetlić komunikat to można wykorzystać np bazowy abstrakcyjny handler i wzorzec template method

np. coś takiego

abstract class OperationHandlerBase<TInput, TResult>
    {
        protected abstract TResult Handle(TInput input);

        public TResult Execute(TInput input)
        {
           LogActionExecutionStart();
           var result = Handle(input);
           LogActionExecutionEnd();
           LogActionExecutionResult(result);
           LogActionExecutionState(result);
           return result;

        }

        protected virtual void LogActionExecutionStart(/*params*/)
        {
        }

        protected virtual void LogActionExecutionEnd(/*params*/)
        {
        }

        protected virtual void LogActionExecutionResult(TResult result)
        {

        }

        protected virtual void LogActionExecutionState(TResult result)
        {
        }
    }

    class SampleHandler : OperationHandlerBase<int, string>
    {
        protected override string Handle(int input)
        {
            return input.ToString();
        }
    }

    class SampleService
    {
        private readonly SampleHandler _handler;

        public SampleService(SampleHandler handler)
        {
            _handler = handler;
        }

        public string GetString(int input)
        {
            return _handler.Execute(input);
        }
    }

Tutaj w przykładowej klasie 'Sample handler' możesz przeciążyć metody logujące stan i wykonać tam co tylko chcesz.

edytowany 3x, ostatnio: var, 2019-07-18 20:36

Pozostało 580 znaków

2019-07-19 02:11
1
bakunet napisał(a):

A pobieranie z serwisu poprzedni post

Przecież w Twoim kodzie kluczem słownika jest stały string z _methodList a nie jakiś callerName.
Nadal proponuję użyć nameof:

var messages = new Dictionary<string, string>
{
    [nameof(Klasa.EnterTheQty)] = "\nError when getting the number.",
    [nameof(Klasa.GenerateRandomData)] = "\nError when generating random data.",
    [nameof(Klasa.SerializeData)] = "\nError when serializing data.",
};

Przy czym zamiast takich kombinacji, ja bym chyba po prostu użył loggera i go ręcznie wywoływał, przynajmniej wiadomo wtedy na jakim etapie jest wołany.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
edytowany 1x, ostatnio: somekind, 2019-07-19 02:14
Skorzystam z Twojego przykładu dlatego, że ciężko się testuje [CallerMemberName] i trzeba wtedy korzystać ze StackFrame : https://stackoverflow.com/q/18706295 i też podoba mi się fakt, że przy zmianie nazwy metody IDE aktualizuje/przypomina o aktualizacji jej w kolekcji - bakunet 2019-07-19 07:10

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0