2015-08-26 4 views
0

Следующего код является небезопасным (или, по крайней мере, я предполагаю, что это такое):комбинируя используя операторы и замки

using (SQLiteConnection connection = new SQLiteConnection("path")) { 
    MyTableCreationHelper.CreateTable(connection, "tableName")); 
} 

Проблема заключается в том, что если другой поток пытается создать таблицу на другое подключение к тому же пути , база данных будет заблокирована, поэтому будет выбрано исключение. Чтобы предотвратить это, я могу сделать следующее:

object lockObject = myLockHelper.GetUniqueObjectForLocking("path"); //does what it claims to do; implementation not shown 
lock (lockObject) { 
    using (SQLiteConnection connection = new SQLiteConnection("path")) { 
    MyTableCreationHelper.CreateTable(connection, "tableName")); 
    } 
} 

Теперь код безопасен, но и clunkier, как я должен обернуть используя в замке каждый раз. Мой вопрос в том, есть ли способ объединить использование и блокировку, чтобы сделать его менее неуклюжим?

В идеале это было бы сделано так, чтобы это не зависело от того, что наша внутренняя операция включает в себя SQLiteConnection. Другими словами, запись блокировки для SQLiteConnection является решением, отличным от идеала, потому что если в следующий раз моя блокировка не будет включать SQLite, проблема будет повторяться.

+0

Вместо того чтобы помещать блокировку, я думаю, вы должны написать try-catch внутри 'using (var connection = new ...) {'. Попробуйте создать таблицу, и если она выдает исключение, вы знаете, что таблица уже существует. Еще лучше использовать нечто вроде 'If (tableExists)' добавить строки в таблицу, иначе создать его. – displayName

+0

Вы уверены, что база данных будет метать, для разных «tableName»? Кажется, что-то, что было бы легко сериализуемо. –

+0

Он меня бросил. Я не уверен, почему - он был пойман на общем уровне, где это уже не было очевидным. –

ответ

2

Вы могли бы иметь общий класс-оболочку, которая принимает параметр IDisposable и тот же параметр строки, который делает блокировку и на которую с помощью можно использовать

например (непроверенный код):

public class LockWrapper<T>:IDisposable 
    where T:IDisposable 
{ 
    T obj; 
    object lockObject ; 

    public LockWrapper(T obj, string Name) 
     :this(()=>obj, Name) 
    { 
    } 


    public LockWrapper(Func<T> objcreator, string Name) 
    { 
     lockObject = myLockHelper.GetUniqueObjectForLocking("path"); 
     Monitor.Enter(lockObject); 
     this.obj = objcreator(); 
    } 

    public T Object{get{return obj;}} 

    public void Dispose() 
    { 
     try 
     { 
      obj.Dispose(); 
     } 
     finally 
     { 
      Monitor.Exit(lockObject); 
     } 
    } 
} 

//helper inside a static class 

public static LockWrapper<T> StartLock(this T obj, string LockName) 
    where T:IDisposable 
{ 
    return new LockWrapper<T>(obj, LockName); 
} 

Это было введено непосредственно в SO, поэтому даже не знаю, допустил ли я синтаксические ошибки, но идея остается прежней, объект-оболочка заботится о блокировке.

Вызов будет что-то вроде:

using(var lck = new SQLiteConnection("path").StartLock("path")) 
    MyTableCreationHelper.CreateTable(lck.Object, "tableName")); 

Другой способ пойти лямбда весь путь и использовать что-то вроде

public static void RunLocked<T>(Func<T> objCreator, Action<T> run, string LockName) 
    where T:IDisposable 
{ 
    lock(getlockobject(LockName)) 
    { 
     using(var obj = objCreator()) 
     { 
      run(obj); 
     } 
    } 
} 

Но звонок будет несколько менее интуитивно:

RunLocked(()=> new SQLiteConnection("path"), 
      connection => MyTableCreationHelper.CreateTable(connection , "tableName"), 
      "path");