Reputation: 4440
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!
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
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
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
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
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
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
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