2013-09-25 108 views
0

我正在使用SQLite(v1.0.88.0)和Dapper通過額外的閉包表來存儲一些分層數據。我已經在SQLite中啓用了foreign_keys支持,但它對我完全不起作用。
下面是一個說明了幾個問題,我最小的示例代碼:外鍵約束問題(分層數據)

using System.Data.SQLite; 
using System.IO; 
using Dapper; 

class Program { 
    static string db = "test.db"; 
    static void Main(string[] args) { 
     if(!File.Exists(db)) 
      SQLiteConnection.CreateFile(db); 
     using(SQLiteConnection c = new SQLiteConnection("Data Source=" + db)) { 
      string initializationQuery = 
       "PRAGMA foreign_keys = ON;" + // enable FK 
       "DROP TABLE IF EXISTS Departments;" + 
       "DROP TABLE IF EXISTS Departments_treePath;" + 
       "CREATE TABLE IF NOT EXISTS Departments (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT);" + 
       "CREATE TABLE IF NOT EXISTS Departments_treePath (ancestor INTEGER, descendant INTEGER, level INTEGER, " + 
       "PRIMARY KEY (ancestor, descendant)," + 
       "CONSTRAINT ancestor_ref FOREIGN KEY(ancestor) REFERENCES Departments(ID) ON DELETE CASCADE," + 
       "CONSTRAINT descendant_ref FOREIGN KEY(descendant) REFERENCES Departments(ID) ON DELETE CASCADE);"; 
      c.Execute(initializationQuery); 

      long idA = AddNode(c, 0, "A"); // ID=1 
      long idB = AddNode(c, idA, "B"); // ID=2 
      long idC = AddNode(c, idB, "C"); // ID=3 

      // 1) It works , but it should not, because there is no ID=7 (FK fails) 
      c.Execute("INSERT INTO Departments_treePath (ancestor,descendant) VALUES (7,7)"); 
      // 2) It works, but as far as i can see from SQLite DataBase Browser it does not delete all the references within the Departments_treePath table (cascade delete fails) 
      c.Execute("DELETE FROM Departments WHERE [email protected];", new { id = idC }); 
     } 
    } 
    static long AddNode(SQLiteConnection c, long ancestorID, string name) { 
     string query = "BEGIN;" + 
         "INSERT OR ROLLBACK INTO Departments (Name) VALUES(@Name);" + 
         "CREATE TEMP TABLE _ID AS SELECT last_insert_rowid() AS id;" + 
         "INSERT INTO Departments_treePath (ancestor, descendant, level) " + 
         "SELECT t.ancestor, (SELECT id FROM _ID), t.level + 1 FROM Departments_treePath AS t " + 
         "WHERE t.descendant = @ancestor " + 
         "UNION ALL SELECT id , id, 0 FROM _ID;" + 
         "SELECT id FROM _ID; DROP TABLE _ID;" + 
         "END;"; 
     return System.Linq.Enumerable.First(c.Query<long>(query, new { ancestor = ancestorID, Name = name })); 
    } 
} 

我在SQL/SQLite的新的,似乎我已經失去了一些東西。請指導我。

+0

對我的作品(在命令行上)。你確定'Execute'或'Query'可以執行多個語句嗎? –

+0

感謝您的參與。是的,他們可以執行多個語句,因爲它只是包裝標準的dbconnection功能。 – DmitryG

回答

0

我得到(突然!!!)如何使FK工作在演示的示例。我已經立即嘗試過了。而WOW,它適用於我最新的Dapper/SQLite。

當然,我已經完成了多年前的原始項目,但我相信對原始行爲的明確描述可能對未來某個人有所幫助。

非工作約束的原因是,在執行查詢時,Dapper 保留連接的狀態並標記。因此,當連接初始關閉時(如原始示例),它將在命令執行前再次打開,然後在完成後關閉。任何編譯指示都會丟失。

如果通過c.Open()打開連接,所有設置將在命令執行之間保留(這就是爲什麼所有的東西都可以在控制檯中用於@CL的原因)。所以這是第一個和最簡單的解決

using(SQLiteConnection c = new SQLiteConnection("Data Source=" + db).OpenAndReturn()) { 
    // ... commands 
} 

至於關閉連接的替代方案,您應該加上「PRAGMA foreign_keys = ON」;語句轉換成每個查詢:

c.Execute("PRAGMA foreign_keys = ON; INSERT INTO Departments_treePath (ancestor,descendant) VALUES (7,7)"); 

執行在連接級別的FK約束:

SQLiteConnection c = new SQLiteConnection("Data Source=" + db + ";foreign keys=true;")