Polaczenie serwisu z modelem w generyczny sposob

0

W zalaczniku jest jak wyglada proces

Step jest abstrakcyjna bazowa klasa ktora ma jakies tam swoje informacje a klasy ktore z niej dziedzicza maja jeszcze kilka dodatkowych wlasciwosci

determinate which step it is jest prostym switch casem

                    switch (step)
                    {
                        case HttpRequestStep httpRequestStep:

                            _logger.LogInformation($"Executing httpRequestStep");
                            isHandled = await _provider
                                .GetService<IStepHandler<HttpRequestStep>>()
                                .HandleAsync(message, httpRequestStep, profile);

                            break;

                        case WaitStep waitStep:

                            _logger.LogInformation($"Executing waitStep");
                            isHandled = await _provider
                                .GetService<IStepHandler<WaitStep>>()
                                .HandleAsync(message, waitStep, profile);

                            break;

                        case EmailStep emailStep:

                            _logger.LogInformation($"Executing emailStep");
                            isHandled = await _provider
                                .GetService<IStepHandler<EmailStep>>()
                                .HandleAsync(message, emailStep, profile);

                            break;

                        default:
                            throw new ArgumentOutOfRangeException(nameof(step));
                    }

Pomijam, ze serwisy sa pobierane w metodzie, ale tez lepiej pokazuje problem.
Ogolnie to ciagle robi wszystko to samo. Wiec myslalem jakby to zrobic by usunac tego switch case'a i miec po prostu

isHandled = await StepHandler.HandleAsync(message, step, profile)

Problemem ktory sie pojawia, to skad mialby StepHandler wiedziec ktory handler wywolac?

Myslalem zeby przy rejestracji uzyc jakos refleksji i polaczyc to jakos ze soba... ale to rozwiazanie mi sie nie usmiecha.

Wiem, ze widzialem juz kiedys na ten problem rozwiazanie, ale nie moge go znalezc ani przypomniec ani wymyslec cos

Tak kolejne Step beda dodawane, wiec nie chce za kazdym razem rozszerzac switch case ;)

1

Dlaczego nie uśmiecha ci się refleksja?
Można stworzyć mechanizm który będzie praktycznie bezobsługowy, np. coś takiego (to akurat może nie zadziałać :D):

    public abstract class Step
    {
    }

    [StepHandlerType(typeof(HttpRequestStepHandler))]
    public class HttpRequestStep : Step
    {
    }
    
    [AttributeUsage(AttributeTargets.Class)]
    public class StepHandlerTypeAttribute : Attribute
    {
        public Type Type { get; }

        public StepHandlerTypeAttribute(Type type)
        {
            if(!typeof(IStepHandler).IsAssignableFrom(type)) throw new ArgumentException(nameof(type));
            Type = type;
        }
    }

    public interface IStepHandler
    {
        void Handle(Step step);
    }
    
    public class HttpRequestStepHandler : IStepHandler
    {
        public void Handle(Step step)
        {
            throw new NotImplementedException();
        }
    }

    public class StepHandlerProvider
    {
        private static readonly Dictionary<Type, Type> TypeRepository = new Dictionary<Type, Type>();

        public static void Register()
        {
            var stepTypes = Assembly.GetExecutingAssembly().GetTypes().Where(x =>
                typeof(Step).IsAssignableFrom(x) && x.IsDefined(typeof(StepHandlerTypeAttribute)));

            foreach (var stepType in stepTypes)
            {
                TypeRepository.Add(stepType, stepType.GetCustomAttribute<StepHandlerTypeAttribute>().Type);

            }
        }

        public IStepHandler GetStepHandler(Step step)
        {
            var stepType = step.GetType();
            
            //na przykład tak
            return  Activator.CreateInstance(TypeRepository[stepType], null) as IStepHandler;
        }
    }


    public class Demo
    {
        private readonly StepHandlerProvider _stepHandlerProvider;

        public Demo(StepHandlerProvider stepHandlerProvider)
        {
            _stepHandlerProvider = stepHandlerProvider;
        }

        public void Handle(Step step)
        {
            var handler = _stepHandlerProvider.GetStepHandler(step);
            handler.Handle(step);

        }
    }

Zamiast bawić się refleksją można też np. ręcznie rejestrować typy w słowniku

0

Użyj wymienionego wyżej MediatR albo jeśli gotowiec nie spełni jakichś dodatkowych wymogów napisz własną implementację wzorca mediator. Jakiś czas temu musiałem coś takiego napisac ponieważ nie mogłem użyć MediatR. Jeśli byś potrzebował mogę wrzucić kod na Github.

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