Javier García
Javier García

Reputation: 1114

How to abstract the execution of a function before and after another

Basically I have the following design:

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();
    }
}

The issue is that I have to call BeginSession and EndSession for each action I want to do in my service(s). I've come up with some solutions to no have to consistently do that every time, but they're very overengineered and I don't like them. How would you folks address this issue? Is there any common design pattern for this?

The main thing that moved me forward to doing the session management within the services was to provide an easy to use set of classes which provide the functionality without having the consumers exploit the interface, by for example opening a session, executing multiple things, and then closing it. Or leaving the session open for too long, etc. But this is a different topic.

Upvotes: 0

Views: 90

Answers (3)

Andrea Rossini
Andrea Rossini

Reputation: 330

I think the accepted answer is too simplistic. What if func breaks, but CloseSession is still necessary? This seems instead a perfect case for applying a Disposable pattern.

You will have to create a Disposable Session wrapper, but it will be worth the pain.

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;
    }
}

This will ensure a session is closed even if an exception is thrown.

More info at: https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose

EDIT: The scoped session constructor may receive an ISessionManager in input, so you can make it testable. The test case would pass a mock object implementing the interface, and yes you can hold a private reference to ISessionManager in the class and null it after closing the session.

Upvotes: 1

Prasanna
Prasanna

Reputation: 305

Since "running with session support" is an application wide concern, you might want to look at what Aspect Oriented Programming can do for you. Please see PostSharp

Another approach is to use Decorator pattern. Consider IInvoicingService interface and classes InvoicingService, InvoicingServiceWithSessionSupport (decorator wrapping InvoicingService). But this might very well increase the number of classes. Your solution with helper method seems more elegant.

Upvotes: 0

Javier García
Javier García

Reputation: 1114

Like most people suggested, I did it with a helper function:

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;
     });
}

Upvotes: 0

Related Questions