Custom database Lock - implemented with IDisposable
The IDisposable was primarily added in order to be able to release acquired unmanaged resources in a deterministic fashion. However it also provides a nice way of releasing other resources in a deterministic fashion as you have found.

I would not say that this is an abuse of the disposable pattern: You acquire a lock which is a resource and you need to release it at a given point in time. For me this is good enough especially since you said it's some kind of db lock which I assume lives outside of the application (that is unmanaged for me). It provides you with native support through using to make sure it's released in case the executing code throws an exception.

However I don't particularly like your two public static methods on there. Now you all of a sudden have two interfaces into the class. If you want a factory like interface then make the DoLock method a proper factory method returning the lock which was acquired (in which case the ctor should become private):

public class MyLock : IDisposable
{
    private string _id;

    private MyLock(string id)
    {
        _id = id;
        DoLock();
    }

    public static MyLock Acquire(string id)
    {
        return new MyLock(id);
    }

    private void DoLock()
    {
        // get the lock
    }

    public void Release()
    {
        // release it
    }

    public void Dispose()
    {
        Release();
    }
}


While you could do without a public Release method having Dispose calling Release is semantically useful in case you can't use using and need to release the lock explicitly. In this case the code will be clearer when you call Release rather than Dispose. The .NET stream classes do a similar thing (where Dispose calls Close).

Some things to consider:


Add an "object is disposed" flag so you don't try to release the same lock twice.
Add a finalizer to try and release the lock in case it was not properly released. Add  a Debug.Assert at least for that case. Note that adding a finalizer will make all MyLock objects go through the finalizer queue which will add overhead for the GC. You could wrap the finalizer into a #ifdef DEBUG to only do that in debug builds - depends how many of these locks you create (although I guess if you create lots of them you have another problem altogether).