自定义"使用"块

时间:2013-10-28 19:18:25

标签: c#

我正在使用数据库,并且有一种情况我想要关闭其中的功能。关闭该功能看起来像这样......

DatabaseContext.Advanced.UseOptimisticConcurrency = false;

打开它也很简单。这个功能很好。但我对某些事感到好奇,并想探索它......

是否可以将它包装在“使用”块中,就像处理像dispose和unsafe这样的东西一样?例如......

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

// the feature is turned back on automatically here!

更新

在StackOverflow的优秀人员的帮助下,我现在已经完成了我想要的工作。再次感谢。这是我的工作代码。不要介意详细的文档。我只是那种在头脑中输入所有内容的程序员。

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

然后,在我的实际代码中......

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

6 个答案:

答案 0 :(得分:12)

只需将其包装在IDisposable的类中,您就可以恢复Dispose函数中的功能。

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

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

以上代码只是您需要根据需要进行调整的示例。

答案 1 :(得分:5)

我能想到这样的事情:

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

然后使用它:

bool b;

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

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

为了匹配您的示例,它可能看起来像:

DatabaseContext.Advanced.UseOptimisticConcurrency = false;

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

答案 2 :(得分:0)

您无法使用自定义使用。但是,您可以将逻辑包装在一个实现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;
        }
    }
}

然后你可以像这样使用它:

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

答案 3 :(得分:0)

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
}

答案 4 :(得分:0)

过去我曾经使用过类似的东西。你可以构建它更多,但实质上你传递一个lambda形式的委托,在一个使用块的末尾被调用:

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

通过这种方式,您不需要为要重置的每个可能标志创建新的实现。

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

答案 5 :(得分:0)

您需要的就是这样一个类,它会为您提供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.
}

所以你可以这样说

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