Как абстрагировать выполнение функции до и после другой - PullRequest
0 голосов
/ 05 февраля 2020

В основном у меня следующий дизайн:

public class SessionManager
{
    public void BeginSession() {...}
    public void CloseSession() {...}
}

public class InvoicingService
{
    private SessionManager SessionManager { get; set; }

    public InvoicingService(SessionManager sm)
    {
         SessionManager = sm;
    }

    public void CreateInvoices(Order order)
    {
         SessionManager.BeginSession();

         // do stuff

         SessionManager.CloseSession();
    }
}

Проблема заключается в том, что мне приходится звонить BeginSession и EndSession для каждого действия, которое я хочу выполнить в своих службах. Я придумал несколько решений, чтобы не приходилось постоянно делать это каждый раз, но они очень переоснащены, и они мне не нравятся. Как бы вы решили эту проблему? Есть ли какой-то общий шаблон проектирования для этого?

Главное, что подтолкнуло меня к выполнению управления сессиями в службах, было предоставление простого в использовании набора классов, которые предоставляют функциональность, не позволяя потребителям использовать интерфейс, например, открыв сеанс, выполняя несколько вещей, а затем закрывая его. Или оставить сеанс открытым слишком долго и т. Д. c. Но это другая топи c.

Ответы [ 3 ]

1 голос
/ 11 февраля 2020

Я думаю, что принятый ответ слишком прост c. Что если func сломается, но CloseSession все еще необходимо? Вместо этого это кажется идеальным случаем для применения одноразового шаблона.

Вам нужно будет создать оболочку одноразового сеанса, но это будет стоить боли.

public class ScopedSession: IDisposable
{
    private bool disposed = false;

    public ScopedSession()
    {
        BeginSession();
    }

    public ~ScopedSession()
    {
        Dispose(false);
    }

    public Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private Dispose(bool disposing)
    {
        if (disposed) return; 

        if (disposing) CloseSession();

        disposed = true;
    }
}

public Invoice CreateInvoices(Order order)
{
    using (var s = new ScopedSession())
    {
        // Do stuff
        return invoice;
    }
}

Это обеспечит сеанс закрывается, даже если выдается исключение.

Дополнительная информация по адресу: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose

РЕДАКТИРОВАТЬ: конструктор сеанса с областью действия может получить ISessionManager на входе, так что вы можете сделать это тестируемым Тестовый пример передаст фиктивный объект, реализующий интерфейс, и да, вы можете хранить закрытую ссылку на ISessionManager в классе и обнулять ее после закрытия сеанса.

0 голосов
/ 11 февраля 2020

Поскольку «работа с поддержкой сеансов» является проблемой всего приложения, вы можете посмотреть, что может сделать для вас Аспектно-ориентированное программирование. Пожалуйста, смотрите PostSharp

Другой подход заключается в использовании шаблона Decorator. Рассмотрим интерфейс IInvoicingService и классы InvoicingService, InvoicingServiceWithSessionSupport (декоратор, обертывающий InvoicingService). Но это вполне может увеличить количество классов. Ваше решение с помощью вспомогательного метода кажется более элегантным.

0 голосов
/ 07 февраля 2020

Как и предлагали большинство людей, я сделал это с помощью вспомогательной функции:

private T RunWithinSession<T>(Func<T> func)
{
    BeginSession();
    var result = func();
    CloseSession();
    return result;
}

public Invoice CreateInvoices(Order order)
{
     return RunWithinSession(() =>
     {
          // Do stuff
          return invoice;
     });
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...