2013-02-08 108 views
2

我有可能是網絡驅動器上的數據庫。 有兩件事情,我想實現:SQLite的連接策略

  1. 當第一個用戶以只讀模式連接到它(他不 有位置的讀寫訪問,或者數據庫 只讀),其他用戶也必須使用只讀連接(如果他們具有RW訪問,則即使爲 )。
  2. 當第一個用戶在RW模式連接到它,別人無法 連接到數據庫的。

我正在使用SQLite,併發性不應該是問題,因爲數據庫不應該被超過10人同時使用。

更新:這是一個我正在努力工作的示例,所以我可以在程序本身中實現它。幾乎所有東西都可以改變。

UPDATE:現在,當我終於明白了什麼@CL。告訴我,我做了它的工作,這是更新的代碼。

using System.Diagnostics; 
using System.Linq; 
using System.IO; 
using DbSample.Domain; 
using DbSample.Infrastructure; 
using NHibernate.Linq; 
using NHibernate.Util; 


namespace DbSample.Console 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IDatabaseContext databaseContext = null; 

      databaseContext = new SqliteDatabaseContext(args[1]); 

     var connection = LockDB(args[1]); 
     if (connection == null) return; 

     var sessionFactory = databaseContext.CreateSessionFactory(); 
     if (sessionFactory != null) 
     { 

      int insertCount = 0; 

      while (true) 
      { 

       try 
       { 

        using (var session = sessionFactory.OpenSession(connection)) 
        { 
         string result; 
         session.FlushMode = NHibernate.FlushMode.Never; 

         var command = session.Connection.CreateCommand(); 

         command.CommandText = "PRAGMA locking_mode=EXCLUSIVE"; 
         command.ExecuteNonQuery(); 


         using (var transaction = session.BeginTransaction(ReadCommited)) 
         { 
          bool update = false; 
          bool delete = false; 
          bool read = false; 
          bool readall = false; 
          int op = 0; 
          System.Console.Write("\nMenu of the day:\n1: update\n2: delete\n3: read\n4: read all\n0: EXIT\n\nYour choice: "); 
          op = System.Convert.ToInt32(System.Console.ReadLine()); 
          if (op == 1) 
           update = true; 
          else if (op == 2) 
           delete = true; 
          else if (op == 3) 
           read = true; 
          else if (op == 4) 
           readall = true; 
          else if (op == 0) 
           break; 
          else System.Console.WriteLine("Are you retarded? Can't you read?"); 




          if (delete) 
          { 
           System.Console.Write("Enter the ID of the object to delete: "); 
           var objectToRemove = session.Get<MyObject>(System.Convert.ToInt32(System.Console.ReadLine())); 

           if (!(objectToRemove == null)) 
           { 
            session.Delete(objectToRemove); 
            System.Console.WriteLine("Deleted {0}, ID: {1}", objectToRemove.MyName, objectToRemove.Id); 
            deleteCount++; 
           } 
           else 
            System.Console.WriteLine("\nObject not present in the database!\n"); 


          } 

          else if (update) 
          { 
           System.Console.Write("How many objects to add/update? "); 
           int number = System.Convert.ToInt32(System.Console.ReadLine()); 
           number += insertCount; 
           for (; insertCount < number; insertCount++) 
           { 

            var myObject = session.Get<MyObject>(insertCount + 1); 

            if (myObject == null) 
            { 
             myObject = new MyObject 
              { 
               MtName = "Object" + insertCount, 
               IdLegacy = 0, 
                          }; 
             session.Save(myObject); 
             System.Console.WriteLine("Added {0}, ID: {1}", myObject.MyName, myObject.Id); 
            } 
            else 
            { 
             session.Update(myObject); 
             System.Console.WriteLine("Updated {0}, ID: {1}", myObject.MyName, myObject.Id); 
            } 
           } 

          } 

          else if (read) 
          { 

           System.Console.Write("Enter the ID of the object to read: "); 
           var objectToRead = session.Get<MyObject>(System.Convert.ToInt32(System.Console.ReadLine())); 
           if (!(objectToRead == null)) 
            System.Console.WriteLine("Got {0}, ID: {1}", objectToRead.MyName, objectToRead.Id); 
           else 
            System.Console.WriteLine("\nObject not present in the database!\n"); 

          } 

          else if (readall) 
          { 

           System.Console.Write("How many objects to read? "); 
           int number = System.Convert.ToInt32(System.Console.ReadLine()); 
           for (int i = 0; i < number; i++) 
           { 
            var objectToRead = session.Get<MyObject>(i + 1); 
            if (!(objectToRead == null)) 
             System.Console.WriteLine("Got {0}, ID: {1}", objectToRead.MyName, objectToRead.Id); 
            else 
             System.Console.WriteLine("\nObject not present in the database! ID: {0}\n", i + 1); 


           } 

          } 
          update = false; 
          delete = false; 
          read = false; 
          readall = false; 

          transaction.Commit(); 
         } 
        }  
       } 
       catch (System.Exception e) 
       { 
        throw e; 
       } 


      } 
      sessionFactory.Close(); 
     } 

    } 

    private static SQLiteConnection LockDbNew(string database) 
    { 
     var fi = new FileInfo(database); 
     if (!fi.Exists) 
      return null; 
     var builder = new SQLiteConnectionStringBuilder { DefaultTimeout = 1, DataSource = fi.FullName, Version = 3 }; 

     var connectionStr = builder.ToString(); 
     var connection = new SQLiteConnection(connectionStr) { DefaultTimeout = 1 }; 
     var cmd = new SQLiteCommand(connection); 

     connection.Open(); 

     // try to get an exclusive lock on the database 
     try 
     { 
      cmd.CommandText = "PRAGMA locking_mode = EXCLUSIVE; BEGIN EXCLUSIVE; COMMIT;"; 
      cmd.ExecuteNonQuery(); 
     } 
     // if we can't get the exclusive lock, it could mean 3 things 
     // 1: someone else has locked the database 
     // 2: we don't have a write acces to the database location 
     // 3: database itself is a read-only file 
     // So, we try to connect as read-only 
     catch (Exception) 
     { 
      // we try to set the SHARED lock 
      try 
      { 
       // first we clear the locks 
       cmd.CommandText = "PRAGMA locking_mode = NORMAL"; 
       cmd.ExecuteNonQuery(); 
       cmd.CommandText = "SELECT COUNT(*) FROM MyObject"; 
       cmd.ExecuteNonQuery(); 

       // then set the SHARED lock on the database 
       cmd.CommandText = "PRAGMA locking_mode = EXCLUSIVE"; 
       cmd.ExecuteNonQuery(); 
       cmd.CommandText = "SELECT COUNT(*) FROM MyObject"; 
       cmd.ExecuteNonQuery(); 

       readOnly = true; 
      } 
      catch (Exception) 
      { 
       // if we can't set EXCLUSIVE nor SHARED lock, someone else has opened the DB in read-write mode and we can't connect at all 
       connection.Close(); 
       return null; 
      } 

     } 
     return connection; 
    } 

} 

}

回答

2

設置PRAGMA locking_mode=EXCLUSIVE防止SQLite的從交易結束後,釋放它的鎖。

+0

是的,這將解決第二部分,但第一部分呢?你有什麼想法? – gajo357 2013-02-11 08:14:11

+0

共享(只讀)鎖可防止寫入鎖定。 – 2013-02-11 09:30:42

+0

這與解決方案非常接近,但我遇到了一個問題。如果我將它設置爲EXCLUSIVE,則在第一次讀取之後獲得SHARED鎖定,這對於所有用戶都能夠讀取它是很好的。但是,如果我嘗試寫幾次讀後,我會得到「數據庫已鎖定」異常,因爲SHARED鎖在數據庫中,即使它仍然是相同的用戶。我在每個操作之後都執行Transaction.Commit(),並且Session.FlushMode = Commit。 – gajo357 2013-02-11 13:01:29

1

我不知道這是否可以在分貝,但應用程序來完成; 您可以設置一個全局變量(不確定它是否是網絡或桌面應用程序)來檢查是否有人連接並且他有寫入權限。 之後,您可以檢查其他客戶的狀態。

+0

這是一個桌面應用程序,我不指出道歉。 – gajo357 2013-02-11 08:13:09