Ciel
Ciel

Reputation: 4440

Custom "using" blocks

I am working with a database, and there is a situation where I want to turn off a feature in it. Turning off the feature looks something like this...

DatabaseContext.Advanced.UseOptimisticConcurrency = false;

Turning it on is just as easy. This functions fine. But I was curious about something and wanted to explore it ...

Is it possible to wrap this in a "using" block like we do with things like dispose and unsafe? For instance...

using(DatabaseContext.Advanced.UseOptimisticConcurrency = false){
   // do things!
}

// the feature is turned back on automatically here!

Update

With the help of the great people here at StackOverflow, I have now made the behavior I wanted work perfectly. Thanks again. Here is my working code. Don't mind the verbose documentation. I am just the kind of programmer that types everything in my head out.

using System;

namespace Raven.Client {
    /// <summary>
    /// Used to emulate a series of transactions without optimistic concurrency in RavenDB
    /// </summary>
    /// <remarks>
    /// This has been flagged as an 'abuse' of the IDisposable interface, which is something I will learn more about
    /// and try to find a better solution. The purpose of this class is to make absolutely sure that we never use
    /// a document session without optimistic concurrency unless we are completely certain it is what we want. I
    /// elected to wrap this in a disposable block because that is an easy convention to remember, and it makes the
    /// intention very clear to the others who may see this code.
    /// Topics[0]: http://stackoverflow.com/questions/19643266/custom-using-blocks
    /// Topics[1]: http://stackoverflow.com/questions/2101524/is-it-abusive-to-use-idisposable-and-using-as-a-means-for-getting-scoped-beha/2103158#2103158
    /// Topics[2]: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface
    /// </remarks>
    public class RavenPessimistic : IDisposable {
        private readonly IDocumentSession RavenSession;

        /// <summary>
        /// Disable optimistic concurrency for this exact session only.
        /// </summary>
        /// <param name="session"></param>
        public RavenPessimistic(IDocumentSession session) {
            RavenSession = session;
            RavenSession.Advanced.UseOptimisticConcurrency = false;
        }

        /// <summary>
        /// Enable the optimistic concurrency again, so that we do not
        /// ever use it unintentionally
        /// </summary>
        public void Dispose() {
            RavenSession.Advanced.UseOptimisticConcurrency = true;
        }
    }

    /// <summary>
    /// An extension method to make it more convenient to get to this. This is probably not necessary, but I have been
    /// anxious to see how RavenDB handles extension methods.
    /// </summary>
    public static class RavenSessionExtensions {
        public static RavenPessimistic OpenPessimisticSession(this IDocumentSession documentSession) {
            return new RavenPessimistic(documentSession);
        }
    }
}

Then, in my actual code...

    /// <summary>
    /// Edit the given item prototype.
    /// </summary>
    /// <param name="model">
    /// A unique prototype to edit in the database.
    /// </param>
    /// <returns></returns>
    [HttpPost]
    [Route("items/edit/prototype")]
    public JsonResult Prototype(Models.Items.Prototype model) {
        if (ModelState.IsValid) {
            // go through the prototype and make sure to set all of the
            // mutation names to it.
            foreach (var mutation in model.Mutations) {
                mutation.Name = model.Name;
            }

            // we are updating an entry, instead of creating a new one,
            // so temporarily turn off optimistic concurrency since we
            // are 100% sure we want to overwrite the existing document.
            using (RavenSession.OpenPessimisticSession()) {
                RavenSession.Store(model);
                RavenSession.SaveChanges();
            }

            // if this was successful, then return the result to the client
            return Json(true, JsonRequestBehavior.AllowGet);
        }

        return Json(false, JsonRequestBehavior.AllowGet);
    }

Upvotes: 14

Views: 8830

Answers (6)

dkackman
dkackman

Reputation: 15559

I've used something like this in the past. There is more you could build into it but in essence you pass it a delegate in the form of a lambda guarunteed to be invoked at the end of a using block:

class Program
{
    static void Main(string[] args)
    {
        bool b = false;
        using (new Sentry(() => b = false))
        {
            // do some stuff
            b = true;
            // do other stuff
        }

        System.Diagnostics.Debug.Assert(b == false);
    }
}

class Sentry : IDisposable
{
    private Action _action;
    public Sentry(Action disposeAction)
    {
        _action = disposeAction;
    }

    public void Dispose()
    {
        if (_action != null)
            _action();
    }
}

In this way you don't need a new implementation for every possible flag you want to reset.

using(new Sentry(() => DatabaseContext.Advanced.UseOptimisticConcurrency = false)
{
  //
}

Upvotes: 1

Mayank
Mayank

Reputation: 8852

Just wrap that in a class that is IDisposable and you can revert the functionality in Dispose function.

public class SomeClass : IDisposable
{
     public SomeClass()
     {
          DatabaseContext.Advanced.UseOptimisticConcurrency = false;
     }

     public void Dispose()
     {
         DatabaseContext.Advanced.UseOptimisticConcurrency = true;
     }
}

Above code is just example you need to tweak it according to your needs.

Upvotes: 18

Nicholas Carey
Nicholas Carey

Reputation: 74307

All you need is a class like this that gives you a fluent interface

public class DataBaseOptions : IDisposable
{
    public DataBaseOptions()
    {
        // save initial state here
    }

    public DataBaseOptions OptimisticConcurrency( bool state )
    {
        // set option state
        return this ;
    }
    public DataBaseOptions IsolationLevel( IsolationLevel state )
    {
        // set option state
        return this ;
    }

    public void Dispose()
    {
        // restore initial state here ;
    }
}

public enum IsolationLevel
{
    ReadCommitted   = 0 ,
    ReadUncommitted = 1 ,
    CursorStability = 2 ,
    // etc.
}

so you can say something like

using ( DatabaseOptions options = new DatabaseOptions()
                                  .IsolationLevel( IsolationLevel.ReadUncommited )
                                  .OptimisticConcurrency( true )
) {
     // do something useful
  }

Upvotes: 0

JG in SD
JG in SD

Reputation: 5607

public IDisposable DisableOptimisticConcurrency()
{
    DatabaseContext.Advanced.UseOptimisticConcurrency = false;
    return new DisposeAdapter(() =>
    {
        DatabaseContext.Advanced.UseOptimisticConcurrency = true;
    });
}

public class DisposeAdapter : IDisposable
{
    private readonly Action performOnDispose;
    private int disposed = 0;

    public DisposeAdapter(Action performOnDispose)
    {
        if (performOnDispose == null)
            throw new ArgumentNullException("performOnDispose");
        this.performOnDispose = performOnDispose;
    }

    public void Dispose()
    {
         if (Interlocked.Exchange(ref this.disposed, 1) == 0)
             this.performOnDispose();
    }
}

using (DisableOptimisticConcurrency())
{
    // perform action
}

Upvotes: 0

Uwe Keim
Uwe Keim

Reputation: 40736

I could think of something like that:

public class MyUsing :
    IDisposable
{
    private Action _disposeAction;

    public MyUsing(
        Action disposeAction)
    {
        _disposeAction = disposeAction;
    }

    public void Dispose()
    {
        var h = _disposeAction;
        _disposeAction = null;
        if (h != null)
        {
            h();
        }
    }
}

And then use it like:

bool b;

b = false; // Do something before the block.

using (new MyUsing(delegate { b = true; })) // Do something after the block.
{
    // Do Things.
}

To match your example, it could look like:

DatabaseContext.Advanced.UseOptimisticConcurrency = false;

using (new MyUsing(delegate { 
    DatabaseContext.Advanced.UseOptimisticConcurrency = true; }))
{
    // Do Things.
}

Upvotes: 5

user2674389
user2674389

Reputation: 1143

You can't use custom usings. However, you can wrap your logic in an own class implementing IDisposable.

public class UseOptimisticConcurrencyScope : IDisposable
{
    private DatabaseContext _dbContext;
    private bool _originalValue;

    public UseOptimisticConcurrency(DatabaseContext dbContext)
    {
        _dbContext = dbContext;
        _originalValue = dbContext.Advanced.UseOptimisticConcurrency;

        dbContext.Advanced.UseOptimisticConcurrency = false;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinaliz(this);
    }

    public virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            _dbContext.Advanced.UseOptimisticConcurrency = _originalValue;
        }
    }
}

Then you can use it like this:

using (var scope = new UseOptimisticConcurrencyScope(yourContext))
{
    ...
}

Upvotes: 0

Related Questions