2013-09-25 3 views
0

Я использую SQLite (v1.0.88.0) и Dapper для хранения некоторых иерархических данных через дополнительную таблицу закрытия. Я включил поддержку foreign_keys в SQLite, но мне это совсем не работает.
Вот минимальный пример кода, который демонстрирует несколько моих вопросов:Проблема ограничения внешнего ключа (иерархические данные)

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;") 
Смежные вопросы